diff --git a/.asf.yaml b/.asf.yaml index 0621455e650f..a19e3a60a2ba 100644 --- a/.asf.yaml +++ b/.asf.yaml @@ -50,19 +50,15 @@ github: rebase: false collaborators: - - acs-robot - - kiranchavala - - rajujith - - vishesh92 - - GaOrtiga - - SadiJr - - winterhazel - - rp- + - gpordeus + - erikbocks + - Imvedansh + - Damans227 protected_branches: ~ 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/.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..2af9f54edc44 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,22 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT 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 + +.pre-commit-config.yaml @jbampton +/.github/linters/ @jbampton diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 000000000000..10f05a1adfdf --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -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. +description: "Thank you for reporting a bug!" +name: bug +title: "[SHORT PROBLEM DESCRIPTION]" +labels: bug, needs-triageing +body: +- type: markdown + attributes: + value: "## Welcome, please describe your problem below;" +- type: textarea + attributes: + label: problem + value: The long description of your problem +- type: markdown + attributes: + value: "## What versions of cloudstack and any infra components are you using" +- type: textarea + attributes: + label: versions + value: The versions of ACS, hypervisors, storage, network etc.. +- type: textarea + attributes: + label: The steps to reproduce the bug + value: | + 1. + 2. + 3. + ... +- type: textarea + attributes: + label: "What to do about it?" diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 000000000000..90de11698861 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -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. +description: "Thank you for your new feature idea!" +name: feature +title: "[SHORT FUNCTIONAL DESCRIPTION]" +labels: new +body: +- type: textarea + attributes: + label: "The required feature described as a wish" + value: As a User/Admin/Operator I would like to , ... have the system make my morning coffee. 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/aw/imports/github/gh-aw/94662b1dee8ce96c876ba9f33b3ab8be32de82a4/.github_workflows_shared_reporting.md b/.github/aw/imports/github/gh-aw/94662b1dee8ce96c876ba9f33b3ab8be32de82a4/.github_workflows_shared_reporting.md new file mode 100644 index 000000000000..bc08afb42be9 --- /dev/null +++ b/.github/aw/imports/github/gh-aw/94662b1dee8ce96c876ba9f33b3ab8be32de82a4/.github_workflows_shared_reporting.md @@ -0,0 +1,73 @@ +--- +# Report formatting guidelines +--- + +## Report Structure Guidelines + +### 1. Header Levels +**Use h3 (###) or lower for all headers in your issue report to maintain proper document hierarchy.** + +When creating GitHub issues or discussions: +- Use `###` (h3) for main sections (e.g., "### Test Summary") +- Use `####` (h4) for subsections (e.g., "#### Device-Specific Results") +- Never use `##` (h2) or `#` (h1) in reports - these are reserved for titles + +### 2. Progressive Disclosure +**Wrap detailed test results in `
Section Name` tags to improve readability and reduce scrolling.** + +Use collapsible sections for: +- Verbose details (full test logs, raw data) +- Secondary information (minor warnings, extra context) +- Per-item breakdowns when there are many items + +Always keep critical information visible (summary, critical issues, key metrics). + +### 3. Report Structure Pattern + +1. **Overview**: 1-2 paragraphs summarizing key findings +2. **Critical Information**: Show immediately (summary stats, critical issues) +3. **Details**: Use `
Section Name` for expanded content +4. **Context**: Add helpful metadata (workflow run, date, trigger) + +### Design Principles (Airbnb-Inspired) + +Reports should: +- **Build trust through clarity**: Most important info immediately visible +- **Exceed expectations**: Add helpful context like trends, comparisons +- **Create delight**: Use progressive disclosure to reduce overwhelm +- **Maintain consistency**: Follow patterns across all reports + +### Example Report Structure + +```markdown +### Summary +- Key metric 1: value +- Key metric 2: value +- Status: ✅/⚠️/❌ + +### Critical Issues +[Always visible - these are important] + +
+View Detailed Results + +[Comprehensive details, logs, traces] + +
+ +
+View All Warnings + +[Minor issues and potential problems] + +
+ +### Recommendations +[Actionable next steps - keep visible] +``` + +## Workflow Run References + +- Format run IDs as links: `[§12345](https://github.com/owner/repo/actions/runs/12345)` +- Include up to 3 most relevant run URLs at end under `**References:**` +- Do NOT add footer attribution (system adds automatically) diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000000..41b307863fc3 --- /dev/null +++ b/.github/dependabot.yml @@ -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. + +# 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: "maven" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "daily" + cooldown: + default-days: 7 diff --git a/.github/linters/.flake8 b/.github/linters/.flake8 index f250719ca198..3364ad14f290 100644 --- a/.github/linters/.flake8 +++ b/.github/linters/.flake8 @@ -22,8 +22,11 @@ # E224 Tab after operator # E227 Missing whitespace around bitwise or shift operator # E242 Tab after ',' +# E271 Multiple spaces after keyword +# E272 Multiple spaces before keyword # E273 Tab after keyword # E274 Tab before keyword +# E713 Test for membership should be 'not in' # E742 Do not define classes named 'I', 'O', or 'l' # E743 Do not define functions named 'I', 'O', or 'l' # E901 SyntaxError or IndentationError @@ -37,4 +40,4 @@ exclude = .git, venv -select = E112,E113,E133,E223,E224,E227,E242,E273,E274,E742,E743,E901,E902,W291,W292,W293,W391 +select = E112,E113,E133,E223,E224,E227,E242,E271,E272,E273,E274,E713,E742,E743,E901,E902,W291,W292,W293,W391 diff --git a/.github/linters/.markdown-lint.yml b/.github/linters/.markdown-lint.yml new file mode 100644 index 000000000000..6a8a26f48497 --- /dev/null +++ b/.github/linters/.markdown-lint.yml @@ -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. + +# MD001/heading-increment Heading levels should only increment by one level at a time +MD001: false + +# MD004/ul-style Unordered list style +MD004: false + +# MD007/ul-indent Unordered list indentation +MD007: false + +# MD010/no-hard-tabs Hard tabs +MD010: false + +# MD013/line-length Line length +MD013: false + +# MD014/commands-show-output Dollar signs used before commands without showing output +MD014: false + +# MD022/blanks-around-headings Headings should be surrounded by blank lines +MD022: false + +# MD023/heading-start-left Headings must start at the beginning of the line +MD023: false + +# MD024/no-duplicate-heading Multiple headings with the same content +MD024: false + +# MD025/single-title/single-h1 Multiple top-level headings in the same document +MD025: false + +# MD026/no-trailing-punctuation Trailing punctuation in heading +MD026: false + +# MD028/no-blanks-blockquote Blank line inside blockquote +MD028: false + +# MD029/ol-prefix Ordered list item prefix +MD029: false + +# MD031/blanks-around-fences Fenced code blocks should be surrounded by blank lines +MD031: false + +# MD032/blanks-around-lists Lists should be surrounded by blank lines +MD032: false + +# MD033/no-inline-html Inline HTML +MD033: false + +# MD034/no-bare-urls Bare URL used +MD034: false + +# MD036/no-emphasis-as-heading Emphasis used instead of a heading +MD036: false + +# MD037/no-space-in-emphasis Spaces inside emphasis markers +MD037: false + +# MD040/fenced-code-language Fenced code blocks should have a language specified +MD040: false + +# MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading +MD041: false + +# MD046/code-block-style Code block style +MD046: false + +# MD052/reference-links-images Reference links and images should use a label that is defined +MD052: false + +# MD059/descriptive-link-text Link text should be descriptive +MD059: false diff --git a/.github/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 new file mode 100644 index 000000000000..67cbeaa7cbb3 --- /dev/null +++ b/.github/linters/codespell.txt @@ -0,0 +1,492 @@ +accouns +acheived +acount +actuall +acuiring +acumulate +addin +addreess +addtion +adminstrator +afer +afrer +afterall +againt +ags +algoritm +allo +allocted +alocation +alogrithm +alpha-numeric +alue +ammended +ammount +ans +anull +apche +aplication +apllication +applicatio +apporpriate +appropritate +aqcuire +aqcuired +aquire +aquiring +assiciate +assigne +assoication +assosiate +asssert +astroid +asynchroniously +asyncronous +atleast +atomation +attache +attch +attches +authenciation +authenitcation +authenitication +availiability +avialable +bais +beacause +beacuse +becase +becasue +becaues +behviour +birdge +bject +boardcast +bootstraper +bu +callin +cant +capabilites +capablity +capcity +carrefully +cavaet +chaing +checkd +checkin +childs +choosen +chould +clenup +cliente +clinet +cluser +cna +collison +comman +commited +comparision +comparisions +complient +concious +conectix +confg +configruation +configuable +conneciton +constrait +constraits +containg +contex +continuesly +contro +controler +controll +convinience +coputer +correcponding +correspoding +correspoonds +cosole +coudl +couldnt +craete +craeted +crate +crated +createa +createing +credentail +cros +crresponding +curren +currentl +datas +decalared +declatory +decocdes +decypher +defalut +defaut +defered +definiton +deleteable +dependancy +dependant +dependend +deployement +deply +deplying +dervied +descktop +descrption +deserialzed +desination +detination +detroy +detroying +dettach +dettached +dettaching +diabling +diasbled +dictonary +didnt +differnet +differnt +direcotry +directroy +disbale +discrepency +disover +dissapper +dissassociated +divice +dockin +doesn' +doesnot +doesnt +dont' +doubleclick +dows +eanbled +earch +ect +elemnt +elments +emmited +enble +encryted +enebled +enmpty +entires +environmnet +equivalant +erro +erronous +everytime +excute +execept +execption +exects +execut +executeable +exeeded +exisitng +exisits +existsing +expcted +expection +explicitely +faield +faild +failes +falied +fasion +feild +filenname +fillled +findout +fisrt +fo +fowarding +frist +fro +frontent +fuctionality +genarate +generallly +gernerate +get's +gloabal +gorry +gracefull +gradiant +handeling +hanling +happend +hasing +hasnt +havin +hda +hostanme +hould +hsould +hte +identifers +identifyer +identifyers +igoring +immediatley +implememented +implementor +implementors +implemnt +implict +implmeneted +implmentation +incase +includeing +indecates +infor +informations +informaton +ingore +initalize +initator +inspite +instal +instnace +intefaces +intepret +intereface +interfer +interpretted +intialize +intializes +intializing +invlaid +invokation +isnt +ist +klunky +lable +leve +limite +listner +maintainence +maintenace +maintenence +mamagement +mambers +manaully +manuel +maxium +mergable +mesage +messge +metatdata +milisecond +minumum +mis +modifers +mor +mulitply +multipl +multple +mutliple +nast +nd +neccessary +necesary +netowrk +nin +nodel +nome +noone +notin +numbe +numer +occured +occurence +occuring +offfering +ofthe +omited +onother +opeation +optin +orginal +otherwse +outter +overriden +overwritting +paramater +paramemeter +paramenter +paramete +parametrs +pararmeter +parms +parralels +particualr +passowrd +perfromed +permissble +physcial +plugable +pluging +polcies +policys +poluting +possiblity +potenial +prefered +preffered +pressenter +previleges +primay +priviledged +procuct +programatically +progres +properites +propertie +propertys +propogate +provison +psudo +pyhsical +re-use +readabilty +readd +reccuring +recevied +recieved +recursivelly +redunant +refference +releease +relese +remaning +remore +remvoing +renabling +reponse +reqest +reqiured +reserv +reserverd +reseted +reseting +resorce +responser +resposne +resturns +retreive +retreiving +retrive +retrived +retriving +retrun +retuned +returing +rever +rocessor +roperty +runing +runnign +sate +scalled +scirpt +scrip +seconday +seesion +sepcified +sepcify +seprated +ser +servies +seting +settig +sevices +shoul +shoule +signle +simplier +singature +skiping +snpashot +specied +specifed +specifiy +splitted +spped +standy +statics +stickyness +stil +storeage +strat +streched +strutural +succesfull +successfull +suceessful +suces +suiteable +suppots +suppport +syncronous +syste +tage +te +tempdate +testng +tha +thats +ther +therefor +theres +theses +thi +thorugh +throught +ths +tipically +transction +tring +trough +tyoe +ue +ues +unavailibility +uncommited +uncompressible +uneccessarily +unexepected +unexpect +unkonw +unkown +unneccessary +unparseable +unrecoginized +unsupport +unxpected +uptodate +usera +usign +usin +utlization +vaidate +valule +valus +varibles +verfy +verfying +verifing +virutal +visable +wil +wit +wth diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index aeb701bbcfbd..84020f4a6b06 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,20 +30,19 @@ jobs: build: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - - name: Set up JDK 11 - uses: actions/setup-java@v3 + - name: Set up JDK 17 + uses: actions/setup-java@v5 with: - java-version: '11' - distribution: 'adopt' - architecture: x64 - cache: maven + distribution: 'temurin' + java-version: '17' + cache: 'maven' - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: - python-version: '3.8' + python-version: '3.10' architecture: 'x64' - name: Install Build Dependencies diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1cc286dd60ee..4edd448067ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,12 +29,13 @@ permissions: jobs: build: if: github.repository == 'apache/cloudstack' - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: tests: [ "smoke/test_accounts + smoke/test_account_access smoke/test_affinity_groups smoke/test_affinity_groups_projects smoke/test_annotations @@ -54,6 +55,7 @@ jobs: smoke/test_deploy_vm_with_userdata smoke/test_deploy_vms_in_parallel smoke/test_deploy_vms_with_varied_deploymentplanners + smoke/test_restore_vm smoke/test_diagnostics smoke/test_direct_download smoke/test_disk_offerings @@ -85,7 +87,12 @@ jobs: smoke/test_migration smoke/test_multipleips_per_nic smoke/test_nested_virtualization - smoke/test_set_sourcenat", + smoke/test_set_sourcenat + smoke/test_webhook_lifecycle + smoke/test_purge_expunged_vms + smoke/test_extension_lifecycle + smoke/test_extension_custom_action_lifecycle + smoke/test_extension_custom", "smoke/test_network smoke/test_network_acl smoke/test_network_ipv6 @@ -131,7 +138,9 @@ jobs: smoke/test_usage smoke/test_usage_events smoke/test_vm_deployment_planner + smoke/test_vm_strict_host_tags smoke/test_vm_schedule + smoke/test_deploy_vgpu_enabled_vm smoke/test_vm_life_cycle smoke/test_vm_lifecycle_unmanage_import smoke/test_vm_snapshot_kvm @@ -159,7 +168,8 @@ jobs: component/test_cpu_limits component/test_cpu_max_limits component/test_cpu_project_limits - component/test_deploy_vm_userdata_multi_nic", + component/test_deploy_vm_userdata_multi_nic + component/test_deploy_vm_lease", "component/test_egress_fw_rules component/test_invalid_gw_nm component/test_ip_reservation", @@ -179,7 +189,8 @@ jobs: "component/test_project_usage component/test_protocol_number_security_group component/test_public_ip - component/test_resource_limits", + component/test_resource_limits + component/test_resource_limit_tags", "component/test_regions_accounts component/test_routers component/test_snapshots @@ -205,32 +216,49 @@ jobs: smoke/test_list_volumes"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 0 - - name: Set up JDK - uses: actions/setup-java@v3 + - name: Set up JDK 17 + uses: actions/setup-java@v5 with: - java-version: '11' - distribution: 'adopt' - architecture: x64 - cache: maven + distribution: 'temurin' + java-version: '17' + cache: 'maven' - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: - python-version: '3.8' + 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 + sudo apt-get install -y git uuid-runtime genisoimage netcat-openbsd ipmitool build-essential libgcrypt20 libgpg-error-dev libgpg-error0 libopenipmi0 ipmitool libpython3-dev libssl-dev libffi-dev python3-openssl python3-dev python3-setuptools + + - name: Setup IPMI Tool for CloudStack + run: | + # 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 pycrypto mock flask netaddr pylint pycodestyle six astroid + python3 -m pip install --user --upgrade urllib3 lxml paramiko nose texttable ipmisim pyopenssl pycryptodome mock flask netaddr pylint pycodestyle six astroid pynose - name: Install jacoco dependencies run: | @@ -265,7 +293,7 @@ jobs: - 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 @@ -278,7 +306,7 @@ jobs: - name: Start CloudStack Management Server with Simulator run: | - export MAVEN_OPTS="-Xmx4096m -XX:MaxPermSize=800m -Djava.security.egd=file:/dev/urandom -javaagent:jacoco/lib/jacocoagent.jar=address=*,port=36320,output=tcpserver" + 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" echo -e "\nStarting simulator" set +e mvn -Dsimulator -Dorg.eclipse.jetty.annotations.maxWait=120 -pl :cloud-client-ui jetty:run 2>&1 > /tmp/jetty-log || true & @@ -312,10 +340,11 @@ jobs: echo -e "Simulator CI Test Results: (only failures listed)\n" python3 ./tools/marvin/xunit-reader.py integration-test-results/ - - uses: codecov/codecov-action@v3 + - uses: codecov/codecov-action@v4 with: files: jacoco-coverage.xml fail_ci_if_error: true flags: simulator-marvin-tests verbose: true name: codecov + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 430d62df8def..fbd944a758f9 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -32,15 +32,15 @@ jobs: name: codecov runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 0 - - name: Set up JDK11 - uses: actions/setup-java@v3 + - name: Set up JDK 17 + uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: '11' + java-version: '17' cache: 'maven' - name: Build CloudStack with Quality Checks @@ -49,10 +49,11 @@ jobs: cd nonoss && bash -x install-non-oss.sh && cd .. mvn -P quality -Dsimulator -Dnoredist clean install -T$(nproc) - - uses: codecov/codecov-action@v3 + - uses: codecov/codecov-action@v4 with: files: ./client/target/site/jacoco-aggregate/jacoco.xml fail_ci_if_error: true - flags: unit-tests + 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..74e59aa821d1 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,48 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: CodeQL Analysis +on: + push: + branches: [main] + pull_request: + branches: [main] +permissions: + actions: read + contents: read + security-events: write +jobs: + codeql: + name: CodeQL + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + language: ["actions"] + steps: + - name: Checkout repository + uses: actions/checkout@v5 + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "Security" diff --git a/.github/workflows/daily-repo-status.lock.yml b/.github/workflows/daily-repo-status.lock.yml new file mode 100644 index 000000000000..1d7e7eecd14d --- /dev/null +++ b/.github/workflows/daily-repo-status.lock.yml @@ -0,0 +1,1022 @@ +# +# ___ _ +# / _ \ | | (_) +# | |_| | __ _ ___ _ __ | |_ _ +# | _ |/ _` |/ _ \ '_ \| __| |/ __| +# | | | | (_| | __/ | | | |_| | ( +# \_| |_/\__, |\___|_| |_|\__|_|\___| +# __/ | +# _ _ |___/ +# | | | | / _| | +# | | | | ___ _ __ _ __| |_| | _____ +# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| +# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ +# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ +# +# This file was automatically generated by gh-aw (v0.45.0). DO NOT EDIT. +# +# To update this file, edit githubnext/agentics/workflows/daily-repo-status.md@d19056381ba48cb1f7c78510c23069701fa7ae87 and run: +# gh aw +# 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 +# activity (issues, PRs, discussions, releases, code changes) and +# engaging GitHub issues with productivity insights, community highlights, +# and project recommendations. +# +# Source: githubnext/agentics/workflows/daily-repo-status.md@ +# +# frontmatter-hash: + +name: "Daily Repo Status" +"on": + schedule: + - cron: "25 18 * * *" + # Friendly format: daily (scattered) + workflow_dispatch: + +permissions: {} + +concurrency: + group: "gh-aw-${{ github.workflow }}" + +run-name: "Daily Repo Status" + +jobs: + activation: + runs-on: ubuntu- + permissions: + contents: + outputs: + comment_id: "" + comment_repo: "" + steps: + - name: Setup + uses: github/gh-aw/actions/setup@58d1d157fbac0f1204798500faefc4f7461ebe28 # v0.45. + with: + destination: /opt/gh-aw/ + - name: Check workflow file + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # + env: + GH_AW_WORKFLOW_FILE: "daily-repo-status.lock.yml" + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs'); + await main(); + + agent: + needs: + runs-on: ubuntu- + permissions: + contents: + issues: + pull-requests: + 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: + GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/ + GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs. + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config. + GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools. + GH_AW_WORKFLOW_ID_SANITIZED: + outputs: + checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} + has_patch: ${{ steps.collect_output.outputs.has_patch }} + model: ${{ steps.generate_aw_info.outputs.model }} + output: ${{ steps.collect_output.outputs.output }} + output_types: ${{ steps.collect_output.outputs.output_types }} + secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} + steps: + - name: Setup + uses: github/gh-aw/actions/setup@58d1d157fbac0f1204798500faefc4f7461ebe28 # v0.45. + with: + destination: /opt/gh-aw/ + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0. + with: + persist-credentials: + - name: Create gh-aw temp + run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir. + - name: Configure Git + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + # Re-authenticate git with GitHub + 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 + id: checkout- + if: | + github.event. + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # + 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('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs'); + await main(); + - name: Generate agentic run + id: + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # + with: + script: | + const fs = require('fs'); + + const awInfo = { + engine_id: "copilot", + engine_name: "GitHub Copilot CLI", + model: process.env.GH_AW_MODEL_AGENT_COPILOT || "", + version: "", + agent_version: "0.0.410", + cli_version: "v0.45.0", + workflow_name: "Daily Repo Status", + experimental: false, + supports_tools_allowlist: true, + supports_http_transport: true, + run_id: context.runId, + run_number: context.runNumber, + run_attempt: process.env.GITHUB_RUN_ATTEMPT, + repository: context.repo.owner + '/' + context.repo.repo, + ref: context.ref, + sha: context.sha, + actor: context.actor, + event_name: context.eventName, + staged: false, + allowed_domains: ["defaults"], + firewall_enabled: true, + awf_version: "v0.18.0", + awmg_version: "v0.1.4", + steps: { + firewall: "squid" + }, + created_at: new Date().toISOString() + }; + + // Write to /tmp/gh-aw directory to avoid inclusion in + const tmpPath = '/tmp/gh-aw/aw_info.json'; + fs.writeFileSync(tmpPath, JSON.stringify(awInfo, null, 2)); + console.log('Generated aw_info.json at:', tmpPath); + console.log(JSON.stringify(awInfo, null, 2)); + + // Set model as output for reuse in other steps/ + core.setOutput('model', awInfo.model); + - name: Validate COPILOT_GITHUB_TOKEN + id: validate- + run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot- + env: + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + - name: Install GitHub Copilot + run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0. + - name: Install awf + run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.18. + - name: Download container + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.18.0 ghcr.io/github/gh-aw-firewall/squid:0.18.0 ghcr.io/github/gh-aw-mcpg:v0.1.4 ghcr.io/github/github-mcp-server:v0.30.3 node:lts- + - name: Write Safe Outputs + run: | + mkdir -p /opt/gh-aw/ + mkdir -p /tmp/gh-aw/ + mkdir -p /tmp/gh-aw/mcp-logs/ + cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' + {"create_issue":{"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + + cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' + [ + { + "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[repo-status] \". Labels [report daily-status] will be automatically added.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "body": { + "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.", + "type": "string" + }, + "labels": { + "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.", + "items": { + "type": "string" + }, + "type": "array" + }, + "parent": { + "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.", + "type": [ + "number", + "string" + ] + }, + "temporary_id": { + "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 8 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.", + "pattern": "^aw_[A-Za-z0-9]{4,8}$", + "type": "string" + }, + "title": { + "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.", + "type": "string" + } + }, + "required": [ + "title", + "body" + ], + "type": "object" + }, + "name": "create_issue" + }, + { + "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "alternatives": { + "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", + "type": "string" + }, + "reason": { + "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).", + "type": "string" + }, + "tool": { + "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.", + "type": "string" + } + }, + "required": [ + "reason" + ], + "type": "object" + }, + "name": "missing_tool" + }, + { + "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "message": { + "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').", + "type": "string" + } + }, + "required": [ + "message" + ], + "type": "object" + }, + "name": "noop" + }, + { + "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "alternatives": { + "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", + "type": "string" + }, + "context": { + "description": "Additional context about the missing data or where it should come from (max 256 characters).", + "type": "string" + }, + "data_type": { + "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.", + "type": "string" + }, + "reason": { + "description": "Explanation of why this data is needed to complete the task (max 256 characters).", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "missing_data" + } + ] + + cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF' + { + "create_issue": { + "defaultMax": 1, + "fields": { + "body": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": + }, + "labels": { + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": + }, + "parent": { + "issueOrPRNumber": + }, + "repo": { + "type": "string", + "maxLength": + }, + "temporary_id": { + "type": "string" + }, + "title": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": + } + } + }, + "missing_tool": { + "defaultMax": 20, + "fields": { + "alternatives": { + "type": "string", + "sanitize": true, + "maxLength": + }, + "reason": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": + }, + "tool": { + "type": "string", + "sanitize": true, + "maxLength": + } + } + }, + "noop": { + "defaultMax": 1, + "fields": { + "message": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": + } + } + } + } + + - name: Generate Safe Outputs MCP Server + id: safe-outputs- + run: | + # Generate a secure random API key (360 bits of entropy, 40+ chars) + # Mask immediately to prevent timing + API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${API_KEY}" + + PORT= + + # Set outputs for next + { + 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 + id: safe-outputs- + env: + DEBUG: '*' + 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: /opt/gh-aw/safeoutputs/tools. + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config. + GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/ + run: | + # Environment variables are set above to prevent template + export + export + export + export + export + export + + bash /opt/gh-aw/actions/start_safe_outputs_server. + + - name: Start MCP + id: start-mcp- + env: + GH_AW_SAFE_OUTPUTS: ${{ env.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 + mkdir -p /tmp/gh-aw/mcp- + + # Export gateway environment variables for MCP config and gateway + export MCP_GATEWAY_PORT="80" + export MCP_GATEWAY_DOMAIN="host.docker.internal" + MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${MCP_GATEWAY_API_KEY}" + export + export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" + mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" + export DEBUG="*" + + export GH_AW_ENGINE="copilot" + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -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_LOCKDOWN -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.1.4' + + mkdir -p /home/runner/. + cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway. + { + "mcpServers": { + "github": { + "type": "stdio", + "container": "ghcr.io/github/github-mcp-server:v0.30.3", + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", + "GITHUB_READ_ONLY": "1", + "GITHUB_TOOLSETS": "context,repos,issues,pull_requests" + } + }, + "safeoutputs": { + "type": "http", + "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT", + "headers": { + "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}" + } + } + }, + "gateway": { + "port": $MCP_GATEWAY_PORT, + "domain": "${MCP_GATEWAY_DOMAIN}", + "apiKey": "${MCP_GATEWAY_API_KEY}", + "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" + } + } + + - name: Generate workflow + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # + with: + script: | + const { generateWorkflowOverview } = require('/opt/gh-aw/actions/generate_workflow_overview.cjs'); + await generateWorkflowOverview(core); + - name: Create prompt with built-in + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt. + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + run: | + bash /opt/gh-aw/actions/create_prompt_first. + cat << 'GH_AW_PROMPT_EOF' > "$GH_AW_PROMPT" + + + cat "/opt/gh-aw/prompts/xpia.md" >> "$GH_AW_PROMPT" + cat "/opt/gh-aw/prompts/temp_folder_prompt.md" >> "$GH_AW_PROMPT" + cat "/opt/gh-aw/prompts/markdown.md" >> "$GH_AW_PROMPT" + cat << 'GH_AW_PROMPT_EOF' >> "$GH_AW_PROMPT" + + GitHub API Access Instructions + + The gh CLI is NOT authenticated. Do NOT use gh commands for GitHub operations. + + + To create or modify GitHub resources (issues, discussions, pull requests, etc.), you MUST call the appropriate safe output tool. Simply writing content will NOT work - the workflow requires actual tool calls. + + Temporary IDs: Some safe output tools support a temporary ID field (usually named temporary_id) so you can reference newly-created items elsewhere in the SAME agent output (for example, using #aw_abc1 in a later body). + + **IMPORTANT - temporary_id format rules:** + - If you DON'T need to reference the item later, OMIT the temporary_id field entirely (it will be auto-generated if needed) + - If you DO need cross-references/chaining, you MUST match this EXACT validation regex: /^aw_[A-Za-z0-9]{3,8}$/ + - Format: aw_ prefix followed by 3 to 8 alphanumeric characters (A-Z, a-z, 0-9, case-insensitive) + - Valid alphanumeric characters: + - INVALID examples: aw_ab (too short), aw_123456789 (too long), aw_test-id (contains hyphen), aw_id_123 (contains underscore) + - VALID examples: aw_abc, aw_abc1, aw_Test123, aw_A1B2C3D4, + - To generate valid IDs: use 3-8 random alphanumeric characters or omit the field to let the system auto- + + Do NOT invent other aw_* formats — downstream steps will reject them with validation errors matching against /^aw_[A-Za-z0-9]{3,8}$/i. + + Discover available tools from the safeoutputs MCP server. + + **Critical**: Tool calls write structured data that downstream jobs process. Without tool calls, follow-up actions will be skipped. + + **Note**: If you made no other safe output tool calls during this workflow execution, call the "noop" tool to provide a status message indicating completion or that no actions were needed. + + + + The following GitHub context information is available for this workflow: + {{#if __GH_AW_GITHUB_ACTOR__ }} + - **actor**: + {{/if}} + {{#if __GH_AW_GITHUB_REPOSITORY__ }} + - **repository**: + {{/if}} + {{#if __GH_AW_GITHUB_WORKSPACE__ }} + - **workspace**: + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} + - **issue-number**: # + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} + - **discussion-number**: # + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} + - **pull-request-number**: # + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} + - **comment-id**: + {{/if}} + {{#if __GH_AW_GITHUB_RUN_ID__ }} + - **workflow-run-id**: + {{/if}} + + + + cat << 'GH_AW_PROMPT_EOF' >> "$GH_AW_PROMPT" + + + cat << 'GH_AW_PROMPT_EOF' >> "$GH_AW_PROMPT" + {{#runtime-import .github/workflows/daily-repo-status.md}} + + - name: Substitute + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt. + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + with: + script: | + const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs'); + + // Call the substitution + return await substitutePlaceholders({ + file: process.env.GH_AW_PROMPT, + substitutions: { + GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, + GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, + 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. + } + }); + - name: Interpolate variables and render + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt. + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs'); + await main(); + - name: Validate prompt + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt. + run: bash /opt/gh-aw/actions/validate_prompt_placeholders. + - name: Print + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt. + run: bash /opt/gh-aw/actions/print_prompt_summary. + - name: Clean git + run: bash /opt/gh-aw/actions/clean_git_credentials. + - name: Execute GitHub Copilot + id: + # Copilot CLI tool arguments (sorted): + timeout-minutes: + run: | + set -o + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-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 --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.18.0 --skip-pull \ + -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_COPILOT:+ --model "$GH_AW_MODEL_AGENT_COPILOT"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio. + env: + COPILOT_AGENT_RUNNER_TYPE: + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config. + GH_AW_MODEL_AGENT_COPILOT: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }} + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt. + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} + GITHUB_WORKSPACE: ${{ github.workspace }} + XDG_CONFIG_HOME: /home/ + - name: Configure Git + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + # Re-authenticate git with GitHub + 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 + if: always() + continue-on-error: + run: | + # Copy Copilot session state files to logs folder for artifact + # This ensures they are in /tmp/gh-aw/ where secret redaction can scan + SESSION_STATE_DIR="$HOME/.copilot/session-state" + LOGS_DIR="/tmp/gh-aw/sandbox/agent/logs" + + if [ -d "$SESSION_STATE_DIR" ]; + echo "Copying Copilot session state files from $SESSION_STATE_DIR to $LOGS_DIR" + mkdir -p "$LOGS_DIR" + cp -v "$SESSION_STATE_DIR"/*.jsonl "$LOGS_DIR/" 2>/dev/null || + echo "Session state files copied successfully" + + echo "No session-state directory found at $SESSION_STATE_DIR" + + - name: Stop MCP + if: always() + continue-on-error: + 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 /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID" + - name: Redact secrets in + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/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: Upload Safe + if: always() + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0. + with: + name: safe- + path: ${{ env.GH_AW_SAFE_OUTPUTS }} + if-no-files-found: + - name: Ingest agent + id: + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # + env: + GH_AW_SAFE_OUTPUTS: ${{ env.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" + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_API_URL: ${{ github.api_url }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs'); + await main(); + - name: Upload sanitized agent + if: always() && env. + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0. + with: + name: agent- + path: ${{ env.GH_AW_AGENT_OUTPUT }} + if-no-files-found: + - name: Upload engine output + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0. + with: + name: + path: | + /tmp/gh-aw/sandbox/agent/logs/ + /tmp/gh-aw/redacted-urls. + if-no-files-found: + - name: Parse agent logs for step + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # + env: + GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs'); + await main(); + - name: Parse MCP Gateway logs for step + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs'); + await main(); + - name: Print firewall + if: always() + continue-on-error: + env: + AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/ + run: | + # Fix permissions on firewall logs so they can be uploaded as + # AWF runs with sudo, creating files owned by + sudo chmod -R a+r /tmp/gh-aw/sandbox/firewall/logs 2>/dev/null || + # 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; + awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" + + echo 'AWF binary not installed, skipping firewall log summary' + + - name: Upload agent + if: always() + continue-on-error: + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0. + with: + name: agent- + path: | + /tmp/gh-aw/aw-prompts/prompt. + /tmp/gh-aw/aw_info. + /tmp/gh-aw/mcp-logs/ + /tmp/gh-aw/sandbox/firewall/logs/ + /tmp/gh-aw/agent-stdio. + /tmp/gh-aw/agent/ + if-no-files-found: + + conclusion: + needs: + - + - + - + - + if: (always()) && (needs.agent.result != 'skipped') + runs-on: ubuntu- + permissions: + contents: + issues: + outputs: + 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 + uses: github/gh-aw/actions/setup@58d1d157fbac0f1204798500faefc4f7461ebe28 # v0.45. + with: + destination: /opt/gh-aw/ + - name: Download agent output + continue-on-error: + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0. + with: + name: agent- + path: /tmp/gh-aw/safeoutputs/ + - name: Setup agent output environment + run: | + mkdir -p /tmp/gh-aw/safeoutputs/ + find "/tmp/gh-aw/safeoutputs/" -type f - + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" + - name: Process No-Op + id: + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_NOOP_MAX: + GH_AW_WORKFLOW_NAME: "Daily Repo Status" + GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/daily-repo-status.md@d19056381ba48cb1f7c78510c23069701fa7ae87" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/tree/d19056381ba48cb1f7c78510c23069701fa7ae87/workflows/daily-repo-status.md" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/noop.cjs'); + await main(); + - name: Record Missing + id: + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Daily Repo Status" + GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/daily-repo-status.md@d19056381ba48cb1f7c78510c23069701fa7ae87" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/tree/d19056381ba48cb1f7c78510c23069701fa7ae87/workflows/daily-repo-status.md" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/missing_tool.cjs'); + await main(); + - name: Handle Agent + id: + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Daily Repo Status" + GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/daily-repo-status.md@d19056381ba48cb1f7c78510c23069701fa7ae87" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/tree/d19056381ba48cb1f7c78510c23069701fa7ae87/workflows/daily-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_SECRET_VERIFICATION_RESULT: ${{ needs.agent.outputs.secret_verification_result }} + GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs'); + await main(); + - name: Handle No-Op + id: + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Daily Repo Status" + GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/daily-repo-status.md@d19056381ba48cb1f7c78510c23069701fa7ae87" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/tree/d19056381ba48cb1f7c78510c23069701fa7ae87/workflows/daily-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_MESSAGE: ${{ steps.noop.outputs.noop_message }} + GH_AW_NOOP_REPORT_AS_ISSUE: "true" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs'); + await main(); + + detection: + needs: + if: needs.agent.outputs.output_types != '' || needs.agent.outputs.has_patch == 'true' + runs-on: ubuntu- + permissions: {} + concurrency: + group: "gh-aw-copilot-${{ github.workflow }}" + timeout-minutes: + outputs: + success: ${{ steps.parse_results.outputs.success }} + steps: + - name: Setup + uses: github/gh-aw/actions/setup@58d1d157fbac0f1204798500faefc4f7461ebe28 # v0.45. + with: + destination: /opt/gh-aw/ + - name: Download agent + continue-on-error: + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0. + with: + name: agent- + path: /tmp/gh-aw/threat-detection/ + - name: Download agent output + continue-on-error: + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0. + with: + name: agent- + path: /tmp/gh-aw/threat-detection/ + - name: Echo agent output + env: + AGENT_OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }} + run: | + echo "Agent output-types: $AGENT_OUTPUT_TYPES" + - name: Setup threat + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # + env: + WORKFLOW_NAME: "Daily 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('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs'); + await main(); + - name: Ensure threat-detection directory and + run: | + mkdir -p /tmp/gh-aw/threat- + touch /tmp/gh-aw/threat-detection/detection. + - name: Validate COPILOT_GITHUB_TOKEN + id: validate- + run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot- + env: + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + - name: Install GitHub Copilot + run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0. + - name: Execute GitHub Copilot + id: + # Copilot CLI tool arguments (sorted): + # --allow-tool shell(cat) + # --allow-tool shell(grep) + # --allow-tool shell(head) + # --allow-tool shell(jq) + # --allow-tool shell(ls) + # --allow-tool shell(tail) + # --allow-tool shell(wc) + timeout-minutes: + run: | + set -o + COPILOT_CLI_INSTRUCTION="$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" + mkdir -p /tmp/ + mkdir -p /tmp/gh-aw/ + mkdir -p /tmp/gh-aw/agent/ + mkdir -p /tmp/gh-aw/sandbox/agent/logs/ + copilot --add-dir /tmp/ --add-dir /tmp/gh-aw/ --add-dir /tmp/gh-aw/agent/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --allow-tool 'shell(cat)' --allow-tool 'shell(grep)' --allow-tool 'shell(head)' --allow-tool 'shell(jq)' --allow-tool 'shell(ls)' --allow-tool 'shell(tail)' --allow-tool 'shell(wc)' --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$COPILOT_CLI_INSTRUCTION"${GH_AW_MODEL_DETECTION_COPILOT:+ --model "$GH_AW_MODEL_DETECTION_COPILOT"} 2>&1 | tee /tmp/gh-aw/threat-detection/detection. + env: + COPILOT_AGENT_RUNNER_TYPE: + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + GH_AW_MODEL_DETECTION_COPILOT: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || '' }} + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt. + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} + GITHUB_WORKSPACE: ${{ github.workspace }} + XDG_CONFIG_HOME: /home/ + - name: Parse threat detection + id: + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs'); + await main(); + - name: Upload threat detection + if: always() + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0. + with: + name: threat-detection. + path: /tmp/gh-aw/threat-detection/detection. + if-no-files-found: + + safe_outputs: + needs: + - + - + if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.detection.outputs.success == 'true') + runs-on: ubuntu- + permissions: + contents: + issues: + timeout-minutes: + env: + GH_AW_ENGINE_ID: "copilot" + GH_AW_WORKFLOW_ID: "daily-repo-status" + GH_AW_WORKFLOW_NAME: "Daily Repo Status" + GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/daily-repo-status.md@d19056381ba48cb1f7c78510c23069701fa7ae87" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/tree/d19056381ba48cb1f7c78510c23069701fa7ae87/workflows/daily-repo-status.md" + outputs: + 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 + uses: github/gh-aw/actions/setup@58d1d157fbac0f1204798500faefc4f7461ebe28 # v0.45. + with: + destination: /opt/gh-aw/ + - name: Download agent output + continue-on-error: + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0. + with: + name: agent- + path: /tmp/gh-aw/safeoutputs/ + - name: Setup agent output environment + run: | + mkdir -p /tmp/gh-aw/safeoutputs/ + find "/tmp/gh-aw/safeoutputs/" -type f - + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" + - name: Process Safe + id: + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"labels\":[\"report\",\"daily-status\"],\"max\":1,\"title_prefix\":\"[repo-status] \"},\"missing_data\":{},\"missing_tool\":{}}" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs'); + await main(); diff --git a/.github/workflows/daily-repo-status.md b/.github/workflows/daily-repo-status.md new file mode 100644 index 000000000000..431b4afb91a6 --- /dev/null +++ b/.github/workflows/daily-repo-status.md @@ -0,0 +1,54 @@ +--- +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 + +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 + +safe-outputs: + create-issue: + title-prefix: "[repo-status] " + labels: [report, daily-status] +source: githubnext/agentics/workflows/daily-repo-status.md@d19056381ba48cb1f7c78510c23069701fa7ae87 +--- + +# Daily 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..af6cbf49f5ef 100644 --- a/.github/workflows/docker-cloudstack-simulator.yml +++ b/.github/workflows/docker-cloudstack-simulator.yml @@ -47,7 +47,7 @@ jobs: - name: Set Docker repository name run: echo "DOCKER_REPOSITORY=apache" >> $GITHUB_ENV - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - 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/issue-triage-agent.lock.yml b/.github/workflows/issue-triage-agent.lock.yml new file mode 100644 index 000000000000..2410f7b9e457 --- /dev/null +++ b/.github/workflows/issue-triage-agent.lock.yml @@ -0,0 +1,1016 @@ +# +# ___ _ _ +# / _ \ | | (_) +# | |_| | __ _ ___ _ __ | |_ _ ___ +# | _ |/ _` |/ _ \ '_ \| __| |/ __| +# | | | | (_| | __/ | | | |_| | (__ +# \_| |_/\__, |\___|_| |_|\__|_|\___| +# __/ | +# _ _ |___/ +# | | | | / _| | +# | | | | ___ _ __ _ __| |_| | _____ ____ +# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| +# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ +# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ +# +# This file was automatically generated by gh-aw (v0.45.0). DO NOT EDIT. +# +# To update this file, edit github/gh-aw/.github/workflows/issue-triage-agent.md@94662b1dee8ce96c876ba9f33b3ab8be32de82a4 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/ +# +# +# Source: github/gh-aw/.github/workflows/issue-triage-agent.md@94662b1dee8ce96c876ba9f33b3ab8be32de82a4 +# +# Resolved workflow manifest: +# Imports: +# - github/gh-aw/.github/workflows/shared/reporting.md@94662b1dee8ce96c876ba9f33b3ab8be32de82a4 +# +# frontmatter-hash: 7bc83974fa1e47c12b40c3333872e4126711d5c6624022cc78b76047289d8b63 + +name: "Issue Triage Agent" +"on": + schedule: + - cron: "0 14 * * 1-5" + workflow_dispatch: + +permissions: {} + +concurrency: + group: "gh-aw-${{ github.workflow }}" + +run-name: "Issue Triage Agent" + +jobs: + activation: + runs-on: ubuntu-slim + permissions: + contents: read + outputs: + comment_id: "" + comment_repo: "" + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@58d1d157fbac0f1204798500faefc4f7461ebe28 # v0.45.0 + with: + destination: /opt/gh-aw/actions + - name: Check workflow file timestamps + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_WORKFLOW_FILE: "issue-triage-agent.lock.yml" + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs'); + await main(); + + agent: + needs: activation + runs-on: ubuntu-latest + permissions: + contents: read + issues: read + 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_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json + GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json + GH_AW_WORKFLOW_ID_SANITIZED: issuetriageagent + outputs: + has_patch: ${{ steps.collect_output.outputs.has_patch }} + model: ${{ steps.generate_aw_info.outputs.model }} + output: ${{ steps.collect_output.outputs.output }} + output_types: ${{ steps.collect_output.outputs.output_types }} + secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@58d1d157fbac0f1204798500faefc4f7461ebe28 # v0.45.0 + with: + destination: /opt/gh-aw/actions + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Create gh-aw temp directory + run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh + - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + # 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: Generate agentic run info + id: generate_aw_info + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const fs = require('fs'); + + const awInfo = { + engine_id: "copilot", + engine_name: "GitHub Copilot CLI", + model: process.env.GH_AW_MODEL_AGENT_COPILOT || "", + version: "", + agent_version: "0.0.410", + cli_version: "v0.45.0", + workflow_name: "Issue Triage Agent", + experimental: false, + supports_tools_allowlist: true, + supports_http_transport: true, + run_id: context.runId, + run_number: context.runNumber, + run_attempt: process.env.GITHUB_RUN_ATTEMPT, + repository: context.repo.owner + '/' + context.repo.repo, + ref: context.ref, + sha: context.sha, + actor: context.actor, + event_name: context.eventName, + staged: false, + allowed_domains: ["defaults"], + firewall_enabled: true, + awf_version: "v0.18.0", + awmg_version: "v0.1.4", + steps: { + firewall: "squid" + }, + created_at: new Date().toISOString() + }; + + // Write to /tmp/gh-aw directory to avoid inclusion in PR + const tmpPath = '/tmp/gh-aw/aw_info.json'; + fs.writeFileSync(tmpPath, JSON.stringify(awInfo, null, 2)); + console.log('Generated aw_info.json at:', tmpPath); + console.log(JSON.stringify(awInfo, null, 2)); + + // Set model as output for reuse in other steps/jobs + core.setOutput('model', awInfo.model); + - name: Validate COPILOT_GITHUB_TOKEN secret + id: validate-secret + run: /opt/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: Install GitHub Copilot CLI + run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.410 + - name: Install awf binary + run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.18.0 + - name: Determine automatic lockdown mode for GitHub MCP Server + id: determine-automatic-lockdown + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} + GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} + with: + script: | + const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); + await determineAutomaticLockdown(github, context, core); + - name: Download container images + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.18.0 ghcr.io/github/gh-aw-firewall/squid:0.18.0 ghcr.io/github/gh-aw-mcpg:v0.1.4 ghcr.io/github/github-mcp-server:v0.30.3 node:lts-alpine + - name: Write Safe Outputs Config + run: | + mkdir -p /opt/gh-aw/safeoutputs + mkdir -p /tmp/gh-aw/safeoutputs + mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs + cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' + {"add_comment":{"max":1},"add_labels":{"allowed":["bug","feature","enhancement","documentation","question","help-wanted","good-first-issue"],"max":3},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + GH_AW_SAFE_OUTPUTS_CONFIG_EOF + cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' + [ + { + "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. CONSTRAINTS: Maximum 1 comment(s) can be added.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "body": { + "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.", + "type": "string" + }, + "item_number": { + "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). If omitted, the tool will attempt to resolve the target from the current workflow context (triggering issue, PR, or discussion).", + "type": "number" + } + }, + "required": [ + "body" + ], + "type": "object" + }, + "name": "add_comment" + }, + { + "description": "Add labels to an existing GitHub issue or pull request for categorization and filtering. Labels must already exist in the repository. For creating new issues with labels, use create_issue with the labels property instead. CONSTRAINTS: Only these labels are allowed: [bug feature enhancement documentation question help-wanted good-first-issue].", + "inputSchema": { + "additionalProperties": false, + "properties": { + "item_number": { + "description": "Issue or PR number to add labels to. This is the numeric ID from the GitHub URL (e.g., 456 in github.com/owner/repo/issues/456). If omitted, adds labels to the item that triggered this workflow.", + "type": "number" + }, + "labels": { + "description": "Label names to add (e.g., ['bug', 'priority-high']). Labels must exist in the repository.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "name": "add_labels" + }, + { + "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "alternatives": { + "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", + "type": "string" + }, + "reason": { + "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).", + "type": "string" + }, + "tool": { + "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.", + "type": "string" + } + }, + "required": [ + "reason" + ], + "type": "object" + }, + "name": "missing_tool" + }, + { + "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "message": { + "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').", + "type": "string" + } + }, + "required": [ + "message" + ], + "type": "object" + }, + "name": "noop" + }, + { + "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "alternatives": { + "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", + "type": "string" + }, + "context": { + "description": "Additional context about the missing data or where it should come from (max 256 characters).", + "type": "string" + }, + "data_type": { + "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.", + "type": "string" + }, + "reason": { + "description": "Explanation of why this data is needed to complete the task (max 256 characters).", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "missing_data" + } + ] + GH_AW_SAFE_OUTPUTS_TOOLS_EOF + cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF' + { + "add_comment": { + "defaultMax": 1, + "fields": { + "body": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "item_number": { + "issueOrPRNumber": true + } + } + }, + "add_labels": { + "defaultMax": 5, + "fields": { + "item_number": { + "issueOrPRNumber": true + }, + "labels": { + "required": true, + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 + } + } + }, + "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 + } + } + } + } + GH_AW_SAFE_OUTPUTS_VALIDATION_EOF + - 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_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: /opt/gh-aw/safeoutputs/tools.json + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/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_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 /opt/gh-aw/actions/start_safe_outputs_server.sh + + - name: Start MCP Gateway + id: start-mcp-gateway + env: + GH_AW_SAFE_OUTPUTS: ${{ env.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_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }} + 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 /tmp/gh-aw/mcp-config + + # Export gateway environment variables for MCP config and gateway script + export MCP_GATEWAY_PORT="80" + export MCP_GATEWAY_DOMAIN="host.docker.internal" + 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 DEBUG="*" + + export GH_AW_ENGINE="copilot" + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -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_LOCKDOWN -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.1.4' + + mkdir -p /home/runner/.copilot + cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh + { + "mcpServers": { + "github": { + "type": "stdio", + "container": "ghcr.io/github/github-mcp-server:v0.30.3", + "env": { + "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN", + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", + "GITHUB_READ_ONLY": "1", + "GITHUB_TOOLSETS": "issues,labels" + } + }, + "safeoutputs": { + "type": "http", + "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT", + "headers": { + "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}" + } + } + }, + "gateway": { + "port": $MCP_GATEWAY_PORT, + "domain": "${MCP_GATEWAY_DOMAIN}", + "apiKey": "${MCP_GATEWAY_API_KEY}", + "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" + } + } + GH_AW_MCP_CONFIG_EOF + - name: Generate workflow overview + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { generateWorkflowOverview } = require('/opt/gh-aw/actions/generate_workflow_overview.cjs'); + await generateWorkflowOverview(core); + - name: Create prompt with built-in context + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + run: | + bash /opt/gh-aw/actions/create_prompt_first.sh + cat << 'GH_AW_PROMPT_EOF' > "$GH_AW_PROMPT" + + GH_AW_PROMPT_EOF + cat "/opt/gh-aw/prompts/xpia.md" >> "$GH_AW_PROMPT" + cat "/opt/gh-aw/prompts/temp_folder_prompt.md" >> "$GH_AW_PROMPT" + cat "/opt/gh-aw/prompts/markdown.md" >> "$GH_AW_PROMPT" + cat << 'GH_AW_PROMPT_EOF' >> "$GH_AW_PROMPT" + + GitHub API Access Instructions + + The gh CLI is NOT authenticated. Do NOT use gh commands for GitHub operations. + + + To create or modify GitHub resources (issues, discussions, pull requests, etc.), you MUST call the appropriate safe output tool. Simply writing content will NOT work - the workflow requires actual tool calls. + + Temporary IDs: Some safe output tools support a temporary ID field (usually named temporary_id) so you can reference newly-created items elsewhere in the SAME agent output (for example, using #aw_abc1 in a later body). + + **IMPORTANT - temporary_id format rules:** + - If you DON'T need to reference the item later, OMIT the temporary_id field entirely (it will be auto-generated if needed) + - If you DO need cross-references/chaining, you MUST match this EXACT validation regex: /^aw_[A-Za-z0-9]{3,8}$/i + - Format: aw_ prefix followed by 3 to 8 alphanumeric characters (A-Z, a-z, 0-9, case-insensitive) + - Valid alphanumeric characters: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 + - INVALID examples: aw_ab (too short), aw_123456789 (too long), aw_test-id (contains hyphen), aw_id_123 (contains underscore) + - VALID examples: aw_abc, aw_abc1, aw_Test123, aw_A1B2C3D4, aw_12345678 + - To generate valid IDs: use 3-8 random alphanumeric characters or omit the field to let the system auto-generate + + Do NOT invent other aw_* formats — downstream steps will reject them with validation errors matching against /^aw_[A-Za-z0-9]{3,8}$/i. + + Discover available tools from the safeoutputs MCP server. + + **Critical**: Tool calls write structured data that downstream jobs process. Without tool calls, follow-up actions will be skipped. + + **Note**: If you made no other safe output tool calls during this workflow execution, call the "noop" tool to provide a status message indicating completion or that no actions were needed. + + + + The following GitHub context information is available for this workflow: + {{#if __GH_AW_GITHUB_ACTOR__ }} + - **actor**: __GH_AW_GITHUB_ACTOR__ + {{/if}} + {{#if __GH_AW_GITHUB_REPOSITORY__ }} + - **repository**: __GH_AW_GITHUB_REPOSITORY__ + {{/if}} + {{#if __GH_AW_GITHUB_WORKSPACE__ }} + - **workspace**: __GH_AW_GITHUB_WORKSPACE__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} + - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} + - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} + - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} + - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__ + {{/if}} + {{#if __GH_AW_GITHUB_RUN_ID__ }} + - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ + {{/if}} + + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' >> "$GH_AW_PROMPT" + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' >> "$GH_AW_PROMPT" + {{#runtime-import .github/aw/imports/github/gh-aw/94662b1dee8ce96c876ba9f33b3ab8be32de82a4/.github_workflows_shared_reporting.md}} + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' >> "$GH_AW_PROMPT" + {{#runtime-import .github/workflows/issue-triage-agent.md}} + GH_AW_PROMPT_EOF + - name: Substitute placeholders + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + with: + script: | + const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs'); + + // Call the substitution function + return await substitutePlaceholders({ + file: process.env.GH_AW_PROMPT, + substitutions: { + GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, + GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, + 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 + } + }); + - name: Interpolate variables and render templates + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs'); + await main(); + - name: Validate prompt placeholders + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh + - name: Print prompt + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: bash /opt/gh-aw/actions/print_prompt_summary.sh + - name: Clean git credentials + run: bash /opt/gh-aw/actions/clean_git_credentials.sh + - name: Execute GitHub Copilot CLI + id: agentic_execution + # Copilot CLI tool arguments (sorted): + timeout-minutes: 5 + run: | + set -o pipefail + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-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 --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.18.0 --skip-pull \ + -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_COPILOT:+ --model "$GH_AW_MODEL_AGENT_COPILOT"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log + env: + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json + GH_AW_MODEL_AGENT_COPILOT: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }} + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} + GITHUB_WORKSPACE: ${{ github.workspace }} + XDG_CONFIG_HOME: /home/runner + - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + # 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: | + # Copy Copilot session state files to logs folder for artifact collection + # This ensures they are in /tmp/gh-aw/ where secret redaction can scan them + SESSION_STATE_DIR="$HOME/.copilot/session-state" + LOGS_DIR="/tmp/gh-aw/sandbox/agent/logs" + + if [ -d "$SESSION_STATE_DIR" ]; then + echo "Copying Copilot session state files from $SESSION_STATE_DIR to $LOGS_DIR" + mkdir -p "$LOGS_DIR" + cp -v "$SESSION_STATE_DIR"/*.jsonl "$LOGS_DIR/" 2>/dev/null || true + echo "Session state files copied successfully" + else + echo "No session-state directory found at $SESSION_STATE_DIR" + fi + - 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 /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID" + - name: Redact secrets in logs + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/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: Upload Safe Outputs + if: always() + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: safe-output + path: ${{ env.GH_AW_SAFE_OUTPUTS }} + if-no-files-found: warn + - name: Ingest agent output + id: collect_output + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_SAFE_OUTPUTS: ${{ env.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" + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_API_URL: ${{ github.api_url }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs'); + await main(); + - name: Upload sanitized agent output + if: always() && env.GH_AW_AGENT_OUTPUT + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: agent-output + path: ${{ env.GH_AW_AGENT_OUTPUT }} + if-no-files-found: warn + - name: Upload engine output files + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: agent_outputs + path: | + /tmp/gh-aw/sandbox/agent/logs/ + /tmp/gh-aw/redacted-urls.log + if-no-files-found: ignore + - name: Parse agent logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs'); + await main(); + - name: Parse MCP Gateway logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/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 so they can be uploaded as artifacts + # AWF runs with sudo, creating files owned by root + sudo chmod -R a+r /tmp/gh-aw/sandbox/firewall/logs 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: Upload agent artifacts + if: always() + continue-on-error: true + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: agent-artifacts + path: | + /tmp/gh-aw/aw-prompts/prompt.txt + /tmp/gh-aw/aw_info.json + /tmp/gh-aw/mcp-logs/ + /tmp/gh-aw/sandbox/firewall/logs/ + /tmp/gh-aw/agent-stdio.log + /tmp/gh-aw/agent/ + if-no-files-found: ignore + + conclusion: + needs: + - activation + - agent + - detection + - safe_outputs + if: (always()) && (needs.agent.result != 'skipped') + runs-on: ubuntu-slim + permissions: + contents: read + discussions: write + issues: write + pull-requests: write + outputs: + 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 + uses: github/gh-aw/actions/setup@58d1d157fbac0f1204798500faefc4f7461ebe28 # v0.45.0 + with: + destination: /opt/gh-aw/actions + - name: Download agent output artifact + continue-on-error: true + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + with: + name: agent-output + path: /tmp/gh-aw/safeoutputs/ + - name: Setup agent output environment variable + run: | + mkdir -p /tmp/gh-aw/safeoutputs/ + find "/tmp/gh-aw/safeoutputs/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" + - name: Process No-Op Messages + id: noop + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_NOOP_MAX: 1 + GH_AW_WORKFLOW_NAME: "Issue Triage Agent" + GH_AW_WORKFLOW_SOURCE: "github/gh-aw/.github/workflows/issue-triage-agent.md@94662b1dee8ce96c876ba9f33b3ab8be32de82a4" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/github/gh-aw/tree/94662b1dee8ce96c876ba9f33b3ab8be32de82a4/.github/workflows/issue-triage-agent.md" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/noop.cjs'); + await main(); + - name: Record Missing Tool + id: missing_tool + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Issue Triage Agent" + GH_AW_WORKFLOW_SOURCE: "github/gh-aw/.github/workflows/issue-triage-agent.md@94662b1dee8ce96c876ba9f33b3ab8be32de82a4" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/github/gh-aw/tree/94662b1dee8ce96c876ba9f33b3ab8be32de82a4/.github/workflows/issue-triage-agent.md" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/missing_tool.cjs'); + await main(); + - name: Handle Agent Failure + id: handle_agent_failure + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Issue Triage Agent" + GH_AW_WORKFLOW_SOURCE: "github/gh-aw/.github/workflows/issue-triage-agent.md@94662b1dee8ce96c876ba9f33b3ab8be32de82a4" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/github/gh-aw/tree/94662b1dee8ce96c876ba9f33b3ab8be32de82a4/.github/workflows/issue-triage-agent.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: "issue-triage-agent" + GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.agent.outputs.secret_verification_result }} + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs'); + await main(); + - name: Handle No-Op Message + id: handle_noop_message + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Issue Triage Agent" + GH_AW_WORKFLOW_SOURCE: "github/gh-aw/.github/workflows/issue-triage-agent.md@94662b1dee8ce96c876ba9f33b3ab8be32de82a4" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/github/gh-aw/tree/94662b1dee8ce96c876ba9f33b3ab8be32de82a4/.github/workflows/issue-triage-agent.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_MESSAGE: ${{ steps.noop.outputs.noop_message }} + GH_AW_NOOP_REPORT_AS_ISSUE: "true" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs'); + await main(); + + detection: + needs: agent + if: needs.agent.outputs.output_types != '' || needs.agent.outputs.has_patch == 'true' + runs-on: ubuntu-latest + permissions: {} + timeout-minutes: 10 + outputs: + success: ${{ steps.parse_results.outputs.success }} + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@58d1d157fbac0f1204798500faefc4f7461ebe28 # v0.45.0 + with: + destination: /opt/gh-aw/actions + - name: Download agent artifacts + continue-on-error: true + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + with: + name: agent-artifacts + path: /tmp/gh-aw/threat-detection/ + - name: Download agent output artifact + continue-on-error: true + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + with: + name: agent-output + path: /tmp/gh-aw/threat-detection/ + - name: Echo agent output types + env: + AGENT_OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }} + run: | + echo "Agent output-types: $AGENT_OUTPUT_TYPES" + - name: Setup threat detection + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + WORKFLOW_NAME: "Issue Triage Agent" + WORKFLOW_DESCRIPTION: "No description provided" + HAS_PATCH: ${{ needs.agent.outputs.has_patch }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs'); + await main(); + - name: Ensure threat-detection directory and log + run: | + mkdir -p /tmp/gh-aw/threat-detection + touch /tmp/gh-aw/threat-detection/detection.log + - name: Validate COPILOT_GITHUB_TOKEN secret + id: validate-secret + run: /opt/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: Install GitHub Copilot CLI + run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.410 + - name: Execute GitHub Copilot CLI + id: agentic_execution + # Copilot CLI tool arguments (sorted): + # --allow-tool shell(cat) + # --allow-tool shell(grep) + # --allow-tool shell(head) + # --allow-tool shell(jq) + # --allow-tool shell(ls) + # --allow-tool shell(tail) + # --allow-tool shell(wc) + timeout-minutes: 20 + run: | + set -o pipefail + COPILOT_CLI_INSTRUCTION="$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" + mkdir -p /tmp/ + mkdir -p /tmp/gh-aw/ + mkdir -p /tmp/gh-aw/agent/ + mkdir -p /tmp/gh-aw/sandbox/agent/logs/ + copilot --add-dir /tmp/ --add-dir /tmp/gh-aw/ --add-dir /tmp/gh-aw/agent/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --allow-tool 'shell(cat)' --allow-tool 'shell(grep)' --allow-tool 'shell(head)' --allow-tool 'shell(jq)' --allow-tool 'shell(ls)' --allow-tool 'shell(tail)' --allow-tool 'shell(wc)' --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$COPILOT_CLI_INSTRUCTION"${GH_AW_MODEL_DETECTION_COPILOT:+ --model "$GH_AW_MODEL_DETECTION_COPILOT"} 2>&1 | tee /tmp/gh-aw/threat-detection/detection.log + env: + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + GH_AW_MODEL_DETECTION_COPILOT: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || '' }} + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} + GITHUB_WORKSPACE: ${{ github.workspace }} + XDG_CONFIG_HOME: /home/runner + - name: Parse threat detection results + id: parse_results + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs'); + await main(); + - name: Upload threat detection log + if: always() + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: threat-detection.log + path: /tmp/gh-aw/threat-detection/detection.log + if-no-files-found: ignore + + safe_outputs: + needs: + - agent + - detection + if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.detection.outputs.success == 'true') + runs-on: ubuntu-slim + permissions: + contents: read + discussions: write + issues: write + pull-requests: write + timeout-minutes: 15 + env: + GH_AW_WORKFLOW_ID: "issue-triage-agent" + GH_AW_WORKFLOW_NAME: "Issue Triage Agent" + GH_AW_WORKFLOW_SOURCE: "github/gh-aw/.github/workflows/issue-triage-agent.md@94662b1dee8ce96c876ba9f33b3ab8be32de82a4" + GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/github/gh-aw/tree/94662b1dee8ce96c876ba9f33b3ab8be32de82a4/.github/workflows/issue-triage-agent.md" + outputs: + 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 + uses: github/gh-aw/actions/setup@58d1d157fbac0f1204798500faefc4f7461ebe28 # v0.45.0 + with: + destination: /opt/gh-aw/actions + - name: Download agent output artifact + continue-on-error: true + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + with: + name: agent-output + path: /tmp/gh-aw/safeoutputs/ + - name: Setup agent output environment variable + run: | + mkdir -p /tmp/gh-aw/safeoutputs/ + find "/tmp/gh-aw/safeoutputs/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" + - name: Process Safe Outputs + id: process_safe_outputs + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"add_labels\":{\"allowed\":[\"bug\",\"feature\",\"enhancement\",\"documentation\",\"question\",\"help-wanted\",\"good-first-issue\"]},\"missing_data\":{},\"missing_tool\":{}}" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs'); + await main(); diff --git a/.github/workflows/issue-triage-agent.md b/.github/workflows/issue-triage-agent.md new file mode 100644 index 000000000000..06ff227b740d --- /dev/null +++ b/.github/workflows/issue-triage-agent.md @@ -0,0 +1,78 @@ +--- +on: + schedule: 0 14 * * 1-5 + workflow_dispatch: null +permissions: + issues: read +imports: +- github/gh-aw/.github/workflows/shared/reporting.md@94662b1dee8ce96c876ba9f33b3ab8be32de82a4 +safe-outputs: + add-comment: {} + add-labels: + allowed: + - bug + - feature + - enhancement + - documentation + - question + - help-wanted + - good-first-issue +source: github/gh-aw/.github/workflows/issue-triage-agent.md@94662b1dee8ce96c876ba9f33b3ab8be32de82a4 +strict: true +timeout-minutes: 5 +tools: + github: + toolsets: + - issues + - labels +--- +# Issue Triage Agent + +List open issues in ${{ github.repository }} that have no labels. For each unlabeled issue, analyze the title and body, then add one of the allowed labels: `bug`, `feature`, `enhancement`, `documentation`, `question`, `help-wanted`, or `good-first-issue`. + +Skip issues that: +- Already have any of these labels +- Have been assigned to any user (especially non-bot users) + +After adding the label to an issue, mention the issue author in a comment using this format (follow shared/reporting.md guidelines): + +**Comment Template**: +```markdown +### 🏷️ Issue Triaged + +Hi @{author}! I've categorized this issue as **{label_name}** based on the following analysis: + +**Reasoning**: {brief_explanation_of_why_this_label} + +
+View Triage Details + +#### Analysis +- **Keywords detected**: {list_of_keywords_that_matched} +- **Issue type indicators**: {what_made_this_fit_the_category} +- **Confidence**: {High/Medium/Low} + +#### Recommended Next Steps +- {context_specific_suggestion_1} +- {context_specific_suggestion_2} + +
+ +**References**: [Triage run §{run_id}](https://github.com/github/gh-aw/actions/runs/{run_id}) +``` + +**Key formatting requirements**: +- Use h3 (###) for the main heading +- Keep reasoning visible for quick understanding +- Wrap detailed analysis in `
` tags +- Include workflow run reference +- Keep total comment concise (collapsed details prevent noise) + +## Batch Comment Optimization + +For efficiency, if multiple issues are triaged in a single run: +1. Add individual labels to each issue +2. Add a brief comment to each issue (using the template above) +3. Optionally: Create a discussion summarizing all triage actions for that run + +This provides both per-issue context and batch visibility. 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/linter.yml b/.github/workflows/linter.yml deleted file mode 100644 index 784df0cf03ca..000000000000 --- a/.github/workflows/linter.yml +++ /dev/null @@ -1,47 +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: Lint - -on: [pull_request] - -permissions: - contents: read - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -jobs: - pre-commit: - name: Run pre-commit - runs-on: ubuntu-22.04 - steps: - - name: Check Out - uses: actions/checkout@v4 - - name: Install - run: | - python -m pip install --upgrade pip - pip install pre-commit - - name: Set PY - run: echo "PY=$(python -VV | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV - - uses: actions/cache@v3 - 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 diff --git a/.github/workflows/main-sonar-check.yml b/.github/workflows/main-sonar-check.yml index cc27309f8a53..224ea2cde801 100644 --- a/.github/workflows/main-sonar-check.yml +++ b/.github/workflows/main-sonar-check.yml @@ -32,29 +32,29 @@ jobs: name: Main Sonar JaCoCo Build runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 0 - - name: Set up JDK11 - uses: actions/setup-java@v3 + - name: Set up JDK17 + uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: '11' + java-version: '17' cache: 'maven' - name: Cache SonarCloud packages - uses: actions/cache@v3 + uses: actions/cache@v5 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar - name: Cache local Maven repository - uses: actions/cache@v3 + uses: actions/cache@v5 with: path: ~/.m2/repository - key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + key: ${{ runner.os }}-m2-${{ hashFiles('pom.xml', '*/pom.xml', '*/*/pom.xml', '*/*/*/pom.xml') }} restore-keys: | ${{ runner.os }}-m2 diff --git a/.github/workflows/merge-conflict-checker.yml b/.github/workflows/merge-conflict-checker.yml index 860e1c1b5614..a997cb94ccc0 100644 --- a/.github/workflows/merge-conflict-checker.yml +++ b/.github/workflows/merge-conflict-checker.yml @@ -18,8 +18,8 @@ name: "PR Merge Conflict Check" on: push: - pull_request_target: - types: [synchronize] + pull_request: + types: [opened, synchronize, reopened] permissions: # added using https://github.com/step-security/secure-workflows contents: read diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 000000000000..11fe5c068814 --- /dev/null +++ b/.github/workflows/pre-commit.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: pre-commit + +on: [pull_request] + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + pre-commit: + name: Run pre-commit + runs-on: ubuntu-22.04 + steps: + - name: Check Out + uses: actions/checkout@v5 + - name: Install + run: | + python -m pip install --upgrade pip + pip install pre-commit + - name: Set PY + run: echo "PY=$(python -VV | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV + - uses: actions/cache@v5 + with: + path: ~/.cache/pre-commit + key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }} + - name: Run pre-commit + 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 64fa4c3da0ca..d71f4b0852d8 100644 --- a/.github/workflows/rat.yml +++ b/.github/workflows/rat.yml @@ -30,11 +30,11 @@ jobs: build: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 - - name: Set up JDK 11 - uses: actions/setup-java@v3 + - uses: actions/checkout@v5 + - name: Set up JDK 17 + uses: actions/setup-java@v5 with: - java-version: '11' + java-version: '17' distribution: 'adopt' architecture: x64 cache: maven diff --git a/.github/workflows/sonar-check.yml b/.github/workflows/sonar-check.yml index a8282f251454..31fb671cc58f 100644 --- a/.github/workflows/sonar-check.yml +++ b/.github/workflows/sonar-check.yml @@ -33,30 +33,30 @@ jobs: name: Sonar JaCoCo Coverage runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: ref: "refs/pull/${{ github.event.number }}/merge" fetch-depth: 0 - - name: Set up JDK11 - uses: actions/setup-java@v3 + - name: Set up JDK17 + uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: '11' + java-version: '17' cache: 'maven' - name: Cache SonarCloud packages - uses: actions/cache@v3 + uses: actions/cache@v5 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar - name: Cache local Maven repository - uses: actions/cache@v3 + uses: actions/cache@v5 with: path: ~/.m2/repository - key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + key: ${{ runner.os }}-m2-${{ hashFiles('pom.xml', '*/pom.xml', '*/*/pom.xml', '*/*/*/pom.xml') }} restore-keys: | ${{ runner.os }}-m2 diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 000000000000..842e4497a4ad --- /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@v10 + 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@v10 + 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 4d89977adf9c..56b04a6f9c96 100644 --- a/.github/workflows/ui.yml +++ b/.github/workflows/ui.yml @@ -31,12 +31,12 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v5 with: - node-version: 14 + node-version: 16 - name: Env details run: | @@ -55,7 +55,8 @@ jobs: npm run lint npm run test:unit - - uses: codecov/codecov-action@v3 + - uses: codecov/codecov-action@v4 + if: github.repository == 'apache/cloudstack' with: working-directory: ui files: ./coverage/lcov.info @@ -63,3 +64,4 @@ jobs: flags: uitests verbose: true name: codecov + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore index e638a09ae210..abaef83e4555 100644 --- a/.gitignore +++ b/.gitignore @@ -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 @@ -15,92 +15,92 @@ # specific language governing permissions and limitations # under the License. -build/build.number -.lock-wscript -.waf-* -waf-* -target/ -override/ -.metadata -dist/ *~ +*_flymake.js *.bak -cloud-*.tar.bz2 -*.log -*.pyc -*.patch +*.css.map *.egginfo/ *.egg-info/ -*.prefs -build.number -*.log.* -cloud.log.*.* -unittest -deps/cloud.userlibraries -.DS_Store -.idea *.iml -git-remote-https.exe.stackdump -*.swp -tools/devcloud/devcloudbox/.vagrant -tools/cli/cloudmonkey/marvin/ -tools/cli/cloudmonkey/precache.py -tools/marvin/marvin/cloudstackAPI/ -tools/marvin/build/ -tools/cli/build/ -tools/appliance/systemvmtemplate/packer_cache/ +*.iso *.jar -*.war +*.log +*.log.* *.mar -*.iso +*.orig +*.patch +*.prefs +*.pyc +*.qcow2 +*.raw +*.swp *.tar.gz *.tgz *.vscode -*.css.map - -# this ignores _all files starting with '.'. Don't do that! -#.* - -target-eclipse -!.gitignore +*.war +.DS_Store +.checkstyle .classpath +.idea +.lock-wscript +.metadata +.pmd +.pmdruleset.xml +.project +.pydevproject +.reviewboardrc .settings.xml .settings/ -db.properties.override -replace.properties.override -tools/marvin/marvin/cloudstackAPI/* -docs/tmp -docs/publish -docs/runbook/tmp -docs/runbook/publish -.project +.vscode +.waf-* Gemfile.lock -debian/tmp -debian/files -debian/cloudstack-*/* -debian/*.substvars -debian/*.debhelper -replace.properties.tmp +build/build.number +build.number build-indep-stamp +cloud.log.*.* +cloud-*.tar.bz2 configure-stamp -*_flymake.js +db.properties.override +debian/*.debhelper +debian/*.substvars +debian/cloudstack-*/* +debian/files +debian/tmp +deps/cloud.userlibraries +dist/ +docs/publish +docs/runbook/publish +docs/runbook/tmp +docs/tmp engine/storage/integration-test/test-output -tools/apidoc/log/ +git-remote-https.exe.stackdump +node_modules +override/ +plugins/hypervisors/kvm/.pydevproject plugins/network-elements/juniper-contrail/logs/ +replace.properties.override +replace.properties.tmp +scripts/.pydevproject scripts/vm/hypervisor/xenserver/vhd-util -*.orig -tools/appliance/box/ -.reviewboardrc -.checkstyle -.pmd -.pmdruleset.xml -.pydevproject systemvm/.pydevproject +target/ +target-eclipse test/.pydevprojec -plugins/hypervisors/kvm/.pydevproject -scripts/.pydevproject -*.qcow2 -*.raw +tools/apidoc/log/ +tools/appliance/box/ +tools/appliance/systemvmtemplate/packer_cache/ +tools/cli/build/ +tools/cli/cloudmonkey/marvin/ +tools/cli/cloudmonkey/precache.py +tools/devcloud/devcloudbox/.vagrant +tools/marvin/build/ +tools/marvin/marvin/cloudstackAPI/ +tools/marvin/marvin/cloudstackAPI/* +unittest venv -node_modules -.vscode +waf-* + +# this ignores _all files starting with '.'. Don't do that! +#.* + +!.gitignore 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 0848a1247945..cf6f8d39027d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,31 +15,133 @@ # specific language governing permissions and limitations # under the License. --- -default_stages: [commit, push] +default_stages: [pre-commit, pre-push] default_language_version: # force all unspecified Python hooks to run python3 python: python3 -minimum_pre_commit_version: "2.17.0" +minimum_pre_commit_version: "3.2.0" repos: - repo: meta hooks: - id: identity - id: check-hooks-apply + - repo: https://github.com/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: v4.4.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 - id: destroyed-symlinks + - id: detect-aws-credentials + args: [--allow-missing-credentials] - id: detect-private-key exclude: > (?x) ^scripts/vm/systemvm/id_rsa\.cloud$| + ^server/src/test/java/org/apache/cloudstack/network/ssl/CertServiceTest\.java$| ^server/src/test/java/com/cloud/keystore/KeystoreTest\.java$| ^server/src/test/resources/certs/dsa_self_signed\.key$| ^server/src/test/resources/certs/non_root\.key$| @@ -49,26 +151,49 @@ repos: ^server/src/test/resources/certs/rsa_self_signed\.key$| ^services/console-proxy/rdpconsole/src/test/doc/rdp-key\.pem$| ^systemvm/agent/certs/localhost\.key$| - ^systemvm/agent/certs/realhostip\.key$ + ^systemvm/agent/certs/realhostip\.key$| + ^test/integration/smoke/test_ssl_offloading\.py$ - id: end-of-file-fixer - exclude: \.vhd$ - #- id: fix-byte-order-marker + 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|xml)$ - # - id: trailing-whitespace + - id: trailing-whitespace + files: ^(LICENSE|NOTICE)$|\.(bat|cfg|cs|css|gitignore|header|in|install|java|md|properties|py|rb|rc|sh|sql|te|template|txt|ucls|vue|xml|xsl|yaml|yml)$|^cloud-cli/bindir/cloud-tool$|^debian/changelog$ + args: [--markdown-linebreak-ext=md] + exclude: ^services/console-proxy/rdpconsole/src/test/doc/freerdp-debug-log\.txt$ + - repo: https://github.com/codespell-project/codespell + rev: v2.4.1 + 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: 6.1.0 + 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: + - id: markdownlint + name: run markdownlint + description: check Markdown files with markdownlint + args: [--config=.github/linters/.markdown-lint.yml] + types: [markdown] + files: \.(md|mdown|markdown)$ + - 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/.python-version b/.python-version index d70c8f8d89f2..c8cfe3959183 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.6 +3.10 diff --git a/CHANGES.md b/CHANGES.md index ef498f8edf0b..7a670a55a61f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -242,7 +242,6 @@ Bug ID | Description [CLOUDSTACK-7722](https://issues.apache.org/jira/browse/CLOUDSTACK-7722) | add.label: Add button for tags show the label not "Add" text... [CLOUDSTACK-7246](https://issues.apache.org/jira/browse/CLOUDSTACK-7246) | VM deployment failed due to wrong in script name createipalias.sh... - Version 4.4.1 ------------- @@ -276,7 +275,6 @@ Bug ID | Description [CLOUDSTACK-1632](https://issues.apache.org/jira/browse/CLOUDSTACK-1632) | Mistakes in authorizeSecurityGroup* API docs... [CLOUDSTACK-401](https://issues.apache.org/jira/browse/CLOUDSTACK-401) | Storage options missing from table... - Version 4.4.0 ------------- @@ -430,11 +428,11 @@ Bug ID | Description [CLOUDSTACK-6099](https://issues.apache.org/jira/browse/CLOUDSTACK-6099) | live migration is failing for vm deployed using dynamic compute offerings with NPE [CLOUDSTACK-7528](https://issues.apache.org/jira/browse/CLOUDSTACK-7528) | More verbose logging when sending alert fails [CLOUDSTACK-6624](https://issues.apache.org/jira/browse/CLOUDSTACK-6624) | set specifyIpRanges to true if specifyVlan is set to true -[CLOUDSTACK-7404](https://issues.apache.org/jira/browse/CLOUDSTACK-7404) | Failed to start an instance when originating template has been deleted +[CLOUDSTACK-7404](https://issues.apache.org/jira/browse/CLOUDSTACK-7404) | Failed to start an instance when originating template has been deleted [CLOUDSTACK-6531](https://issues.apache.org/jira/browse/CLOUDSTACK-6531) | Stopping the router in case of command failures [CLOUDSTACK-6115](https://issues.apache.org/jira/browse/CLOUDSTACK-6115) | TravisCI configuration [CLOUDSTACK-7405](https://issues.apache.org/jira/browse/CLOUDSTACK-7405) | allowing VR meta-data to be accessed without trailing slash -[CLOUDSTACK-7260](https://issues.apache.org/jira/browse/CLOUDSTACK-7260) | Management server not responding after some time for Vmware due to Oom +[CLOUDSTACK-7260](https://issues.apache.org/jira/browse/CLOUDSTACK-7260) | Management server not responding after some time for Vmware due to Oom [CLOUDSTACK-7038](https://issues.apache.org/jira/browse/CLOUDSTACK-7038) | Add mysql client dependency for mgmt server pkg for debian [CLOUDSTACK-6892](https://issues.apache.org/jira/browse/CLOUDSTACK-6892) | Create separate package for the mysql HA component [CLOUDSTACK-7038](https://issues.apache.org/jira/browse/CLOUDSTACK-7038) | Add mysql client dependency for mgmt server/rpms @@ -449,12 +447,12 @@ Bug ID | Description [CLOUDSTACK-7006](https://issues.apache.org/jira/browse/CLOUDSTACK-7006) | Restore template ID in ROOT volume usages [CLOUDSTACK-6747](https://issues.apache.org/jira/browse/CLOUDSTACK-6747) | test to allow all cidrs on other end of vpc [CLOUDSTACK-6272](https://issues.apache.org/jira/browse/CLOUDSTACK-6272) | Fix recover/restore VM actions -[CLOUDSTACK-6927](https://issues.apache.org/jira/browse/CLOUDSTACK-6927) | store virsh list in list instead of querying libvirt +[CLOUDSTACK-6927](https://issues.apache.org/jira/browse/CLOUDSTACK-6927) | store virsh list in list instead of querying libvirt [CLOUDSTACK-6317](https://issues.apache.org/jira/browse/CLOUDSTACK-6317) | [VMware] Tagged VLAN support broken for Management/Control/Storage traffic [CLOUDSTACK-5891](https://issues.apache.org/jira/browse/CLOUDSTACK-5891) | [VMware] If a template has been registered and "cpu.corespersocket=X" , [CLOUDSTACK-6478](https://issues.apache.org/jira/browse/CLOUDSTACK-6478) | Failed to download Template when having 3 SSVM's in one [CLOUDSTACK-6464](https://issues.apache.org/jira/browse/CLOUDSTACK-6464) | if guest network type is vlan://untagged, and traffic label is used -[CLOUDSTACK-6816](https://issues.apache.org/jira/browse/CLOUDSTACK-6816) | bugfix: cloudstack-setup-management make /root directory's permission 0777 +[CLOUDSTACK-6816](https://issues.apache.org/jira/browse/CLOUDSTACK-6816) | bugfix: cloudstack-setup-management make /root directory's permission 0777 [CLOUDSTACK-6204](https://issues.apache.org/jira/browse/CLOUDSTACK-6204) | applying missed patch [CLOUDSTACK-6472](https://issues.apache.org/jira/browse/CLOUDSTACK-6472) | (4.3 specific) listUsageRecords: Pull information from removed items as well [CLOUDSTACK-5976](https://issues.apache.org/jira/browse/CLOUDSTACK-5976) | Typo in "ssh_keypairs" table's foreign key constraints on the Upgraded Setup @@ -646,22 +644,22 @@ 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 ------------- This is the second major release of CloudStack from within the Apache Software Foundation, and the -first major release as a Top-Level Project (TLP). +first major release as a Top-Level Project (TLP). Build Tool Changes: - * The project now uses Maven 3 exclusively to build. + * The project now uses Maven 3 exclusively to build. New Features: * CLOUDSTACK-101: OVS support in KVM @@ -930,7 +928,6 @@ Security Fixes: * CVE-2012-4501: Apache CloudStack configuration vulnerability - Version 4.0.2 ------------------------ @@ -976,14 +973,13 @@ Issues fixed in this release: * CLOUDSTACK-1845: KVM - storage migration often fails * CLOUDSTACK-1846: KVM - storage pools can silently fail to be unregistered, leading to failure to register later * CLOUDSTACK-2003: Deleting domain while deleted account is cleaning up leaves VMs expunging forever due to 'Failed to update resource count' -* CLOUDSTACK-2090: Upgrade from version 4.0.1 to version 4.0.2 triggers the 4.0.0 to 4.0.1. +* CLOUDSTACK-2090: Upgrade from version 4.0.1 to version 4.0.2 triggers the 4.0.0 to 4.0.1. * CLOUDSTACK-2091: Error in API documentation for 4.0.x. - Version 4.0.1-incubating ------------------------ -This is a bugfix release for Apache CloudStack 4.0.0-incubating, with no new features. +This is a bugfix release for Apache CloudStack 4.0.0-incubating, with no new features. Security Fixes: @@ -1023,7 +1019,6 @@ Bugs fixed in this release: * CLOUDSTACK-961: Installation docs don't detail dependencies for building RPMs * CLOUDSTACK-995: Not able to add the KVM host - Version 4.0.0-incubating ------------------------ @@ -1056,7 +1051,6 @@ Security Fixes: * CVE-2012-4501: Apache CloudStack configuration vulnerability - Updating this file ------------------ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cdfbfe77b7ed..f0678ed76498 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,46 +1,79 @@ -Contributing to Apache CloudStack (ACS) -======================================= + -Bug fixes ---------- +# Contributing to Apache CloudStack (ACS) -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. +## 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 + +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 + +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. +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 ------------------------- -When developing a new feature or making a (major) change to a existing feature you are encouraged to append this to the PendingReleaseNotes file so that the Release Manager can +## 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. When adding information to the PendingReleaseNotes file make sure that you write a good and understandable description of the new feature or change which you have developed. 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) -Fork the repository by clicking on the 'Fork' button on the top right hand side. The fork will happen and you will be taken to your own fork of the repository. Copy the Git repository URL by clicking on the clipboard next to the URL on the right hand side of the page under '**HTTPS** clone URL'. You will paste this URL when doing the following `git clone` command. +Fork the repository by clicking on the 'Fork' button on the top right hand side. The fork will happen, and you will be taken to your own fork of the repository. Copy the Git repository URL by clicking on the clipboard next to the URL on the right hand side of the page under '**HTTPS** clone URL'. You will paste this URL when doing the following `git clone` command. -On your computer, follow these steps to setup a local repository for working on ACS: +On your computer, follow these steps to set up a local repository for working on ACS: ```bash $ git clone https://github.com/YOUR_ACCOUNT/cloudstack.git @@ -51,10 +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. @@ -70,9 +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. @@ -92,13 +120,11 @@ $ 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. -When you are happy with your changes and you are ready to contribute them, you will create a Pull Request on GitHub to do so. This is done by pushing your local changes to your forked repository (default remote name is `origin`) and then initiating a pull request on GitHub. - -Please include JIRA id, detailed information about the bug/feature, what all tests are executed, how the reviewer can test this feature etc. Incase of UI PRs, a screenshot is preferred. +Please include JIRA id, detailed information about the bug/feature, what all tests are executed, how the reviewer can test this feature etc. In case of UI PRs, a screenshot is preferred. > **IMPORTANT:** Make sure you have rebased your `feature_x` branch to include the latest code from `upstream/main` _before_ you do this. @@ -107,7 +133,7 @@ $ git push origin main $ git push origin feature_x ``` -Now that the `feature_x` branch has been pushed to your GitHub repository, you can initiate the pull request. +Now that the `feature_x` branch has been pushed to your GitHub repository, you can initiate the pull request. To initiate the pull request, do the following: @@ -118,9 +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. @@ -134,6 +158,6 @@ $ git branch -D feature_x $ git push origin :feature_x ``` -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 +## 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 620fc1833a82..52f109b0a411 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,46 +1,78 @@ + + # 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 Install tools and dependencies used for development: - # yum -y install git java-11-openjdk java-11-openjdk-devel \ + # yum -y install git java-17-openjdk java-17-openjdk-devel \ mysql mysql-server mkisofs git gcc python MySQL-python openssh-clients wget -Set up Maven (3.6.0): +Set up Maven (3.9.10): - # wget http://www.us.apache.org/dist/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz - # tar -zxvf apache-maven-3.6.3-bin.tar.gz -C /usr/local + # wget https://dlcdn.apache.org/maven/maven-3/3.9.10/binaries/apache-maven-3.9.10-bin.tar.gz + # sudo tar -zxvf apache-maven-3.9.10-bin.tar.gz -C /usr/local # cd /usr/local - # ln -s apache-maven-3.6.3 maven + # sudo ln -s apache-maven-3.9.10 maven # echo export M2_HOME=/usr/local/maven >> ~/.bashrc # or .zshrc or .profile # echo export PATH=/usr/local/maven/bin:${PATH} >> ~/.bashrc # or .zshrc or .profile # source ~/.bashrc -Setup up NodeJS (LTS): +Setup up Node.js 16: - # curl -sL https://rpm.nodesource.com/setup_12.x | sudo bash - + # curl -sL https://rpm.nodesource.com/setup_16.x | sudo -E bash - # sudo yum install nodejs # sudo npm install -g @vue/cli npm-check-updates Start the MySQL service: $ service mysqld start + $ mysql_secure_installation ### Using jenv and/or pyenv for Version Management -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: @@ -78,7 +110,7 @@ Clear old database (if any) and deploy the database schema: Export the following variable if you need to run and debug the management server: - $ export MAVEN_OPTS="-Xmx1024m -XX:MaxPermSize=500m -Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n" + $ export MAVEN_OPTS="-Xmx1024m -XX:MaxMetaspaceSize=500m -Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n" Start the management server: @@ -86,13 +118,33 @@ Start the management server: If this works, you've successfully setup a single server Apache CloudStack installation. -Open the following URL on your browser to access the Management Server UI: - - http://localhost:8080/client/ +To access the Management Server UI, follow the following procedure: The default credentials are; user: admin, password: password and the domain field should be left blank which is defaulted to the ROOT domain. +## To bring up CloudStack UI + +Move to UI Directory + + $ cd /path/to/cloudstack/ui + +To install dependencies. + + $ npm install + +To build the project. + + $ npm run build + +For Development Mode. + + $ npm start + +Make sure to set `CS_URL=http://localhost:8080` on the `.env.local` file on UI. + +You should be able to run the management server on http://localhost:5050 + ## Building with non-redistributable plugins CloudStack supports several plugins that depend on libraries with distribution restrictions. @@ -150,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/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 85c1ac9ec6a9..4a48a4e75917 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -35,17 +35,14 @@ New line separated list of affected versions, commit ID for issues on main branc Information about the configuration if relevant, e.g. basic network, advanced networking, etc. N/A otherwise --> - ##### OS / ENVIRONMENT - ##### SUMMARY - ##### STEPS TO REPRODUCE + +# 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 8293a22973a2..be04436d3ff8 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -22,7 +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 +- [ ] Build/CI +- [ ] Test (unit or integration test code) ### Feature/Enhancement Scale or Bug Severity @@ -39,10 +40,8 @@ This PR... - [ ] Minor - [ ] Trivial - ### Screenshots (if appropriate): - ### How Has This Been Tested? @@ -52,5 +51,4 @@ This PR... - diff --git a/README.md b/README.md index e193913612f1..a5aacb49f6b5 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 @@ -142,7 +189,7 @@ This distribution includes cryptographic software. The country in which you curr reside may have restrictions on the import, possession, use, and/or re-export to another country, of encryption software. BEFORE using any encryption software, please check your country's laws, regulations and policies concerning the import, possession, or use, and -re-export of encryption software, to see if this is permitted. See [The Wassenaar Arrangement](http://www.wassenaar.org/) +re-export of encryption software, to see if this is permitted. See [The Wassenaar Arrangement](http://www.wassenaar.org/) for more information. The U.S. Government Department of Commerce, Bureau of Industry and Security (BIS), has @@ -160,3 +207,11 @@ The following provides more details on the included cryptographic software: * CloudStack makes use of the Bouncy Castle general-purpose encryption library. * CloudStack can optionally interact with and control OpenSwan-based VPNs. * CloudStack has a dependency on and makes use of JSch - a java SSH2 implementation. + +## Star History + +[![Apache CloudStack Star History](https://api.star-history.com/svg?repos=apache/cloudstack&type=Date)](https://www.star-history.com/#apache/cloudstack&Date) + +## Contributors + +[![Apache CloudStack Contributors](https://contrib.rocks/image?repo=apache/cloudstack&anon=0&max=500)](https://github.com/apache/cloudstack/graphs/contributors) diff --git a/agent/bindir/cloud-setup-agent.in b/agent/bindir/cloud-setup-agent.in index 53c6c2f56aa4..dc4582540df9 100755 --- a/agent/bindir/cloud-setup-agent.in +++ b/agent/bindir/cloud-setup-agent.in @@ -6,9 +6,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 @@ -20,9 +20,22 @@ import os import logging import sys import socket + +# ---- This snippet of code adds the sources path and the waf configured PYTHONDIR to the Python path ---- +# ---- We do this so cloud_utils can be looked up in the following order: +# ---- 1) Sources directory +# ---- 2) waf configured PYTHONDIR +# ---- 3) System Python path +for pythonpath in ( + "@PYTHONDIR@", + os.path.join(os.path.dirname(__file__),os.path.pardir,os.path.pardir,"python","lib"), + ): + if os.path.isdir(pythonpath): sys.path.insert(0,pythonpath) +# ---- End snippet of code ---- + from cloudutils.cloudException import CloudRuntimeException, CloudInternalException from cloudutils.utilities import initLoging, bash -from cloudutils.configFileOps import configFileOps +from cloudutils.configFileOps import configFileOps from cloudutils.globalEnv import globalEnv from cloudutils.networkConfig import networkConfig from cloudutils.syscfg import sysConfigFactory @@ -30,35 +43,41 @@ from cloudutils.serviceConfig import configureLibvirtConfig, configure_libvirt_t from optparse import OptionParser + def getUserInputs(): print("Welcome to the CloudStack Agent Setup:") cfo = configFileOps("@AGENTSYSCONFDIR@/agent.properties") oldMgt = cfo.getEntry("host") - mgtSvr = input("Please input the Management Server Hostname/IP-Address:[%s]"%oldMgt) + mgtSvr = input( + "Please input the Management Server Hostname/IP-Address:[%s]" % oldMgt + ) if mgtSvr == "": mgtSvr = oldMgt try: socket.getaddrinfo(mgtSvr, 443) except: - print("Failed to resolve %s. Please input a valid hostname or IP-Address."%mgtSvr) + print( + "Failed to resolve %s. Please input a valid hostname or IP-Address." + % mgtSvr + ) exit(1) oldToken = cfo.getEntry("zone") - zoneToken = input("Please input the Zone Id:[%s]"%oldToken) + zoneToken = input("Please input the Zone Id:[%s]" % oldToken) if zoneToken == "": zoneToken = oldToken oldPod = cfo.getEntry("pod") - podId = input("Please input the Pod Id:[%s]"%oldPod) + podId = input("Please input the Pod Id:[%s]" % oldPod) if podId == "": - podId = oldToken + podId = oldToken oldCluster = cfo.getEntry("cluster") - clusterId = input("Please input the Cluster Id:[%s]"%oldCluster) + clusterId = input("Please input the Cluster Id:[%s]" % oldCluster) if clusterId == "": clusterId = oldCluster @@ -66,18 +85,20 @@ def getUserInputs(): if oldHypervisor == "": oldHypervisor = "kvm" - hypervisor = input("Please input the Hypervisor type kvm/lxc:[%s]"%oldHypervisor) + hypervisor = input("Please input the Hypervisor type kvm/lxc:[%s]" % oldHypervisor) if hypervisor == "": hypervisor = oldHypervisor try: defaultNic = networkConfig.getDefaultNetwork() except: - print("Failed to get default route. Please configure your network to have a default route") + print( + "Failed to get default route. Please configure your network to have a default route" + ) exit(1) defNic = defaultNic.name - network = input("Please choose which network used to create VM:[%s]"%defNic) + network = input("Please choose which network used to create VM:[%s]" % defNic) if network == "": if defNic == "": print("You need to specify one of Nic or bridge on your system") @@ -87,7 +108,8 @@ def getUserInputs(): return [mgtSvr, zoneToken, network, podId, clusterId, hypervisor] -if __name__ == '__main__': + +if __name__ == "__main__": initLoging("@AGENTLOGDIR@/setup.log") glbEnv = globalEnv() @@ -95,13 +117,23 @@ if __name__ == '__main__': glbEnv.agentMode = "Agent" parser = OptionParser() parser.add_option("-a", action="store_true", dest="auto", help="auto mode") - parser.add_option("-m", "--host", dest="mgt", help="Management server hostname or IP-Address") + parser.add_option( + "-m", "--host", dest="mgt", help="Management server hostname or IP-Address" + ) parser.add_option("-z", "--zone", dest="zone", help="zone id") parser.add_option("-p", "--pod", dest="pod", help="pod id") parser.add_option("-c", "--cluster", dest="cluster", help="cluster id") - parser.add_option("-t", "--hypervisor", default="kvm", dest="hypervisor", help="hypervisor type") + parser.add_option( + "-t", "--hypervisor", default="kvm", dest="hypervisor", help="hypervisor type" + ) parser.add_option("-g", "--guid", dest="guid", help="guid") - parser.add_option("-s", action="store_true", default=False, dest="secure", help="Secure and enable TLS for libvirtd") + parser.add_option( + "-s", + action="store_true", + default=False, + dest="secure", + help="Secure and enable TLS for libvirtd", + ) parser.add_option("--pubNic", dest="pubNic", help="Public traffic interface") parser.add_option("--prvNic", dest="prvNic", help="Private traffic interface") parser.add_option("--guestNic", dest="guestNic", help="Guest traffic interface") @@ -127,15 +159,15 @@ if __name__ == '__main__': glbEnv.pod = userInputs[3] glbEnv.cluster = userInputs[4] glbEnv.hypervisor = userInputs[5] - #generate UUID + # generate UUID glbEnv.uuid = old_config.getEntry("guid") if glbEnv.uuid == "": glbEnv.uuid = bash("uuidgen").getStdout() else: for para, value in list(options.__dict__.items()): if value is None: - print("Missing operand:%s"%para) - print("Try %s --help for more information"%sys.argv[0]) + print("Missing operand:%s" % para) + print("Try %s --help for more information" % sys.argv[0]) sys.exit(1) glbEnv.uuid = options.guid @@ -155,7 +187,7 @@ if __name__ == '__main__': try: syscfg.config() print("CloudStack Agent setup is done!") - except (CloudRuntimeException,CloudInternalException) as e: + except (CloudRuntimeException, CloudInternalException) as e: print(e) print("Try to restore your system:") try: diff --git a/agent/bindir/cloud-ssh.in b/agent/bindir/cloud-ssh.in index e4b3c141a975..a5ea975c2f3a 100644 --- a/agent/bindir/cloud-ssh.in +++ b/agent/bindir/cloud-ssh.in @@ -6,9 +6,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/bindir/libvirtqemuhook.in b/agent/bindir/libvirtqemuhook.in index e17944d83537..4cc6ed7a1d28 100755 --- a/agent/bindir/libvirtqemuhook.in +++ b/agent/bindir/libvirtqemuhook.in @@ -20,6 +20,19 @@ import sys import os import subprocess from threading import Timer + +# ---- This snippet of code adds the sources path and the waf configured PYTHONDIR to the Python path ---- +# ---- We do this so cloud_utils can be looked up in the following order: +# ---- 1) Sources directory +# ---- 2) waf configured PYTHONDIR +# ---- 3) System Python path +for pythonpath in ( + "@PYTHONDIR@", + os.path.join(os.path.dirname(__file__),os.path.pardir,os.path.pardir,"python","lib"), + ): + if os.path.isdir(pythonpath): sys.path.insert(0,pythonpath) +# ---- End snippet of code ---- + from xml.dom.minidom import parse from cloudutils.configFileOps import configFileOps from cloudutils.networkConfig import networkConfig diff --git a/agent/conf/agent.properties b/agent/conf/agent.properties index 6adde7435abb..0dc5b8211e0d 100644 --- a/agent/conf/agent.properties +++ b/agent/conf/agent.properties @@ -209,12 +209,13 @@ hypervisor.type=kvm # the management server would send. # In case of arm64 (aarch64), this will change the machine type to 'virt' and # adds a SCSI and a USB controller in the domain xml. -# Possible values: x86_64 | aarch64 +# Possible values: x86_64 | aarch64 | s390x # If null (default), defaults to the VM's OS architecture #guest.cpu.arch= -# This param will require CPU features on the CPU section. -# The features listed in this property must be separated by a blank space (e.g.: vmx vme) +# Specifies required CPU features for end-user and system VMs. +# These features must be present on the host CPU for VM deployment. +# Multiple features should be separated by whitespace (e.g.: vmx vme). #guest.cpu.features= # Disables memory ballooning on VM guests for overcommit. @@ -286,6 +287,7 @@ hypervisor.type=kvm # The model of Watchdog timer to present to the Guest. # For all models refer to the libvirt documentation. +# PLEASE NOTE: to disable the watchdogs definitions, use value: none #vm.watchdog.model=i6300esb # Action to take when the Guest/Instance is no longer notifying the Watchdog timer. @@ -426,3 +428,32 @@ iscsi.session.cleanup.enabled=false # Instance Conversion from Vmware to KVM through virt-v2v. Enable verbose mode # virtv2v.verbose.enabled=false + +# If set to "true", the agent will register for libvirt domain events, allowing for immediate updates on crashed or +# unexpectedly stopped. Experimental, requires agent restart. +# libvirt.events.enabled=false + +# Implicit host tags managed by agent.properties +# host.tags= + +# Timeout(in seconds) for SSL handshake when agent connects to server. When no value is set then default value of 30s +# will be used +#ssl.handshake.timeout= + +# Wait(in seconds) during agent reconnections. When no value is set then default value of 5s will be used +#backoff.seconds= + +# Timeout (in seconds) to wait for the snapshot reversion to complete. +# revert.snapshot.timeout=10800 + +# Timeout (in seconds) to wait for the incremental snapshot to complete. +# incremental.snapshot.timeout=10800 + +# If set to true, creates VMs as full clones of their templates on KVM hypervisor. Creates as linked clones otherwise. +# create.full.clone=false + +# Instance conversion TMPDIR env var +#convert.instance.env.tmpdir= + +# Instance conversion VIRT_V2V_TMPDIR env var +#convert.instance.env.virtv2v.tmpdir= diff --git a/agent/conf/cloudstack-agent.logrotate.in b/agent/conf/cloudstack-agent.logrotate.in index 2b3dc87f2532..9f22b4bab868 100644 --- a/agent/conf/cloudstack-agent.logrotate.in +++ b/agent/conf/cloudstack-agent.logrotate.in @@ -15,11 +15,13 @@ # specific language governing permissions and limitations # under the License. -/var/log/cloudstack/agent/security_group.log /var/log/cloudstack/agent/resizevolume.log /var/log/cloudstack/agent/rolling-maintenance.log { +/var/log/cloudstack/agent/security_group.log /var/log/cloudstack/agent/resizevolume.log /var/log/cloudstack/agent/rolling-maintenance.log /var/log/cloudstack/agent/agent.out /var/log/cloudstack/agent/agent.err { copytruncate daily rotate 5 compress missingok size 10M + dateext + dateformat -%Y-%m-%d } 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/environment.properties.in b/agent/conf/environment.properties.in index 514161a13fc5..b6cc5bbd9879 100644 --- a/agent/conf/environment.properties.in +++ b/agent/conf/environment.properties.in @@ -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/log4j-cloud.xml.in b/agent/conf/log4j-cloud.xml.in index 44ebd1358af6..84957edca032 100644 --- a/agent/conf/log4j-cloud.xml.in +++ b/agent/conf/log4j-cloud.xml.in @@ -30,7 +30,7 @@ under the License. - + @@ -38,7 +38,7 @@ under the License. - + 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 9caa6d992c80..5ab6cfe17c13 100644 --- a/agent/pom.xml +++ b/agent/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.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 e2ec7d9a17df..275fd41edc34 100644 --- a/agent/src/main/java/com/cloud/agent/Agent.java +++ b/agent/src/main/java/com/cloud/agent/Agent.java @@ -27,23 +27,26 @@ import java.nio.channels.ClosedChannelException; import java.nio.charset.Charset; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Timer; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; import javax.naming.ConfigurationException; -import com.cloud.resource.AgentStatusUpdater; -import com.cloud.resource.ResourceStatusUpdater; -import com.cloud.agent.api.PingAnswer; -import com.cloud.utils.NumbersUtil; import org.apache.cloudstack.agent.lb.SetupMSListAnswer; import org.apache.cloudstack.agent.lb.SetupMSListCommand; import org.apache.cloudstack.ca.PostCertificateRenewalCommand; @@ -55,9 +58,10 @@ import org.apache.cloudstack.utils.security.KeyStoreUtils; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.io.FileUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.logging.log4j.Logger; +import org.apache.commons.lang3.ObjectUtils; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.ThreadContext; import com.cloud.agent.api.AgentControlAnswer; import com.cloud.agent.api.AgentControlCommand; @@ -66,6 +70,9 @@ import com.cloud.agent.api.CronCommand; import com.cloud.agent.api.MaintainAnswer; import com.cloud.agent.api.MaintainCommand; +import com.cloud.agent.api.MigrateAgentConnectionAnswer; +import com.cloud.agent.api.MigrateAgentConnectionCommand; +import com.cloud.agent.api.PingAnswer; import com.cloud.agent.api.PingCommand; import com.cloud.agent.api.ReadyCommand; import com.cloud.agent.api.ShutdownCommand; @@ -75,9 +82,12 @@ import com.cloud.agent.transport.Response; import com.cloud.exception.AgentControlChannelException; import com.cloud.host.Host; +import com.cloud.resource.AgentStatusUpdater; +import com.cloud.resource.ResourceStatusUpdater; import com.cloud.resource.ServerResource; +import com.cloud.utils.NumbersUtil; import com.cloud.utils.PropertiesUtil; -import com.cloud.utils.backoff.BackoffAlgorithm; +import com.cloud.utils.StringUtils; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.NioConnectionException; @@ -87,9 +97,7 @@ import com.cloud.utils.nio.NioClient; import com.cloud.utils.nio.NioConnection; import com.cloud.utils.nio.Task; -import com.cloud.utils.script.OutputInterpreter; import com.cloud.utils.script.Script; -import org.apache.logging.log4j.ThreadContext; /** * @config @@ -113,7 +121,7 @@ public enum ExitStatus { Configuration(66), // Exiting due to configuration problems. Error(67); // Exiting because of error. - int value; + final int value; ExitStatus(final int value) { this.value = value; @@ -124,128 +132,162 @@ public int value() { } } - List _controlListeners = new ArrayList(); + CopyOnWriteArrayList controlListeners = new CopyOnWriteArrayList<>(); - IAgentShell _shell; - NioConnection _connection; - ServerResource _resource; - Link _link; - Long _id; + IAgentShell shell; + NioConnection connection; + ServerResource serverResource; + Link link; + Long id; + String _uuid; + String _name; - Timer _timer = new Timer("Agent Timer"); - Timer certTimer; - Timer hostLBTimer; + ScheduledExecutorService selfTaskExecutor; + ScheduledExecutorService certExecutor; + ScheduledExecutorService hostLbCheckExecutor; - List _watchList = new ArrayList(); - long _sequence = 0; - long _lastPingResponseTime = 0; - long _pingInterval = 0; - AtomicInteger _inProgress = new AtomicInteger(); + CopyOnWriteArrayList> watchList = new CopyOnWriteArrayList<>(); + AtomicLong sequence = new AtomicLong(0); + AtomicLong lastPingResponseTime = new AtomicLong(0L); + long pingInterval = 0; + AtomicInteger commandsInProgress = new AtomicInteger(0); - StartupTask _startup = null; - long _startupWaitDefault = 180000; - long _startupWait = _startupWaitDefault; - boolean _reconnectAllowed = true; - //For time sentitive task, e.g. PingTask - ThreadPoolExecutor _ugentTaskPool; - ExecutorService _executor; + private final AtomicReference startupTask = new AtomicReference<>(); + private static final long DEFAULT_STARTUP_WAIT = 180; + long startupWait = DEFAULT_STARTUP_WAIT; + boolean reconnectAllowed = true; - Thread _shutdownThread = new ShutdownThread(this); + //For time sensitive task, e.g. PingTask + ThreadPoolExecutor outRequestHandler; + ExecutorService requestHandler; - private String _keystoreSetupPath; - private String _keystoreCertImportPath; + Thread shutdownThread = new ShutdownThread(this); - // for simulator use only - public Agent(final IAgentShell shell) { - _shell = shell; - _link = null; + private String keystoreSetupSetupPath; + private String keystoreCertImportScriptPath; - _connection = new NioClient("Agent", _shell.getNextHost(), _shell.getPort(), _shell.getWorkers(), this); + private String hostname; - Runtime.getRuntime().addShutdownHook(_shutdownThread); + protected String getLinkLog(final Link link) { + if (link == null) { + return ""; + } + StringBuilder str = new StringBuilder(); + if (logger.isTraceEnabled()) { + str.append(System.identityHashCode(link)).append("-"); + } + str.append(link.getSocketAddress()); + return str.toString(); + } - _ugentTaskPool = - new ThreadPoolExecutor(shell.getPingRetries(), 2 * shell.getPingRetries(), 10, TimeUnit.MINUTES, new SynchronousQueue(), new NamedThreadFactory( - "UgentTask")); + protected String getAgentName() { + return (serverResource != null && serverResource.isAppendAgentNameToLogs() && + StringUtils.isNotBlank(serverResource.getName())) ? + serverResource.getName() : + "Agent"; + } - _executor = - new ThreadPoolExecutor(_shell.getWorkers(), 5 * _shell.getWorkers(), 1, TimeUnit.DAYS, new LinkedBlockingQueue(), new NamedThreadFactory( - "agentRequest-Handler")); + protected void setupShutdownHookAndInitExecutors() { + logger.trace("Adding shutdown hook"); + Runtime.getRuntime().addShutdownHook(shutdownThread); + selfTaskExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Agent-SelfTask")); + outRequestHandler = new ThreadPoolExecutor(shell.getPingRetries(), 2 * shell.getPingRetries(), 10, TimeUnit.MINUTES, + new SynchronousQueue<>(), new NamedThreadFactory("AgentOutRequest-Handler")); + requestHandler = new ThreadPoolExecutor(shell.getWorkers(), 5 * shell.getWorkers(), 1, TimeUnit.DAYS, + new LinkedBlockingQueue<>(), new NamedThreadFactory("AgentRequest-Handler")); } - public Agent(final IAgentShell shell, final int localAgentId, final ServerResource resource) throws ConfigurationException { - _shell = shell; - _resource = resource; - _link = null; + /** + * Constructor for the {@code Agent} class, intended for simulator use only. + * + *

This constructor initializes the agent with a provided {@link IAgentShell}. + * It sets up the necessary NIO client connection, establishes a shutdown hook, + * and initializes the thread executors. + * + * @param shell the {@link IAgentShell} instance that provides agent configuration and runtime information. + */ + public Agent(final IAgentShell shell) { + this.shell = shell; + this.link = null; + this.connection = new NioClient( + getAgentName(), + this.shell.getNextHost(), + this.shell.getPort(), + this.shell.getWorkers(), + this.shell.getSslHandshakeTimeout(), + this + ); + setupShutdownHookAndInitExecutors(); + } + public Agent(final IAgentShell shell, final int localAgentId, final ServerResource resource) throws ConfigurationException { + this.shell = shell; + serverResource = resource; + link = null; resource.setAgentControl(this); - - final String value = _shell.getPersistentProperty(getResourceName(), "id"); - _id = value != null ? Long.parseLong(value) : null; - logger.info("id is " + (_id != null ? _id : "")); + final String value = shell.getPersistentProperty(getResourceName(), "id"); + _uuid = shell.getPersistentProperty(getResourceName(), "uuid"); + _name = shell.getPersistentProperty(getResourceName(), "name"); + id = value != null ? Long.parseLong(value) : null; + logger.info("Initialising agent [id: {}, uuid: {}, name: {}]", ObjectUtils.defaultIfNull(id, ""), _uuid, _name); final Map params = new HashMap<>(); - // merge with properties from command line to let resource access command line parameters - for (final Map.Entry cmdLineProp : _shell.getCmdLineProperties().entrySet()) { + for (final Map.Entry cmdLineProp : this.shell.getCmdLineProperties().entrySet()) { params.put(cmdLineProp.getKey(), cmdLineProp.getValue()); } - - if (!_resource.configure(getResourceName(), params)) { - throw new ConfigurationException("Unable to configure " + _resource.getName()); + if (!serverResource.configure(getResourceName(), params)) { + throw new ConfigurationException("Unable to configure " + serverResource.getName()); } + ThreadContext.put("agentname", getAgentName()); + final String host = this.shell.getNextHost(); + connection = new NioClient(getAgentName(), host, this.shell.getPort(), this.shell.getWorkers(), + this.shell.getSslHandshakeTimeout(), this); + setupShutdownHookAndInitExecutors(); + logger.info("{} with host = {}, local id = {}", this, host, localAgentId); + } - final String host = _shell.getNextHost(); - _connection = new NioClient("Agent", host, _shell.getPort(), _shell.getWorkers(), this); - - // ((NioClient)_connection).setBindAddress(_shell.getPrivateIp()); - - logger.debug("Adding shutdown hook"); - Runtime.getRuntime().addShutdownHook(_shutdownThread); - - _ugentTaskPool = - new ThreadPoolExecutor(shell.getPingRetries(), 2 * shell.getPingRetries(), 10, TimeUnit.MINUTES, new SynchronousQueue(), new NamedThreadFactory( - "UgentTask")); - - _executor = - new ThreadPoolExecutor(_shell.getWorkers(), 5 * _shell.getWorkers(), 1, TimeUnit.DAYS, new LinkedBlockingQueue(), new NamedThreadFactory( - "agentRequest-Handler")); - logger.info("Agent [id = " + (_id != null ? _id : "new") + " : type = " + getResourceName() + " : zone = " + _shell.getZone() + " : pod = " + _shell.getPod() + - " : workers = " + _shell.getWorkers() + " : host = " + host + " : port = " + _shell.getPort()); + @Override + public String toString() { + return String.format("Agent [id = %s, uuid = %s, name = %s, type = %s, zone = %s, pod = %s, workers = %d, port = %d]", + ObjectUtils.defaultIfNull(id, "new"), + _uuid, + _name, + getResourceName(), + this.shell.getZone(), + this.shell.getPod(), + this.shell.getWorkers(), + this.shell.getPort()); } public String getVersion() { - return _shell.getVersion(); + return shell.getVersion(); } public String getResourceGuid() { - final String guid = _shell.getGuid(); + final String guid = shell.getGuid(); return guid + "-" + getResourceName(); } public String getZone() { - return _shell.getZone(); + return shell.getZone(); } public String getPod() { - return _shell.getPod(); + return shell.getPod(); } protected void setLink(final Link link) { - _link = link; + this.link = link; } public ServerResource getResource() { - return _resource; - } - - public BackoffAlgorithm getBackoffAlgorithm() { - return _shell.getBackoffAlgorithm(); + return serverResource; } public String getResourceName() { - return _resource.getClass().getSimpleName(); + return serverResource.getClass().getSimpleName(); } /** @@ -254,77 +296,69 @@ public String getResourceName() { * agent instances and its inner objects. */ private void scavengeOldAgentObjects() { - _executor.submit(new Runnable() { - @Override - public void run() { - try { - Thread.sleep(2000L); - } catch (final InterruptedException ignored) { - } finally { - System.gc(); - } + requestHandler.submit(() -> { + try { + Thread.sleep(2000L); + } catch (final InterruptedException ignored) { + } finally { + System.gc(); } }); } public void start() { - if (!_resource.start()) { - logger.error("Unable to start the resource: " + _resource.getName()); - throw new CloudRuntimeException("Unable to start the resource: " + _resource.getName()); + if (!serverResource.start()) { + String msg = String.format("Unable to start the resource: %s", serverResource.getName()); + logger.error(msg); + throw new CloudRuntimeException(msg); } - _keystoreSetupPath = Script.findScript("scripts/util/", KeyStoreUtils.KS_SETUP_SCRIPT); - if (_keystoreSetupPath == null) { + keystoreSetupSetupPath = Script.findScript("scripts/util/", KeyStoreUtils.KS_SETUP_SCRIPT); + if (keystoreSetupSetupPath == null) { throw new CloudRuntimeException(String.format("Unable to find the '%s' script", KeyStoreUtils.KS_SETUP_SCRIPT)); } - _keystoreCertImportPath = Script.findScript("scripts/util/", KeyStoreUtils.KS_IMPORT_SCRIPT); - if (_keystoreCertImportPath == null) { + keystoreCertImportScriptPath = Script.findScript("scripts/util/", KeyStoreUtils.KS_IMPORT_SCRIPT); + if (keystoreCertImportScriptPath == null) { throw new CloudRuntimeException(String.format("Unable to find the '%s' script", KeyStoreUtils.KS_IMPORT_SCRIPT)); } try { - _connection.start(); + connection.start(); } catch (final NioConnectionException e) { - logger.warn("NIO Connection Exception " + e); - logger.info("Attempted to connect to the server, but received an unexpected exception, trying again..."); - } - while (!_connection.isStartup()) { - final String host = _shell.getNextHost(); - _shell.getBackoffAlgorithm().waitBeforeRetry(); - _connection = new NioClient("Agent", host, _shell.getPort(), _shell.getWorkers(), this); - logger.info("Connecting to host:" + host); + logger.warn("Attempt to connect to server generated NIO Connection Exception {}, trying again", e.getLocalizedMessage()); + } + while (!connection.isStartup()) { + final String host = shell.getNextHost(); + shell.getBackoffAlgorithm().waitBeforeRetry(); + connection = new NioClient(getAgentName(), host, shell.getPort(), shell.getWorkers(), + shell.getSslHandshakeTimeout(), this); + logger.info("Connecting to host: {}", host); try { - _connection.start(); + connection.start(); } catch (final NioConnectionException e) { - _connection.stop(); - try { - _connection.cleanUp(); - } catch (final IOException ex) { - logger.warn("Fail to clean up old connection. " + ex); - } + stopAndCleanupConnection(false); logger.info("Attempted to connect to the server, but received an unexpected exception, trying again...", e); } } - _shell.updateConnectedHost(); + shell.updateConnectedHost(((NioClient)connection).getHost()); scavengeOldAgentObjects(); - } public void stop(final String reason, final String detail) { - logger.info("Stopping the agent: Reason = " + reason + (detail != null ? ": Detail = " + detail : "")); - _reconnectAllowed = false; - if (_connection != null) { + logger.info("Stopping the agent: Reason = {}{}", reason, (detail != null ? ": Detail = " + detail : "")); + reconnectAllowed = false; + if (connection != null) { final ShutdownCommand cmd = new ShutdownCommand(reason, detail); try { - if (_link != null) { - final Request req = new Request(_id != null ? _id : -1, -1, cmd, false); - _link.send(req.toBytes()); + if (link != null) { + final Request req = new Request(id != null ? id : -1, -1, cmd, false); + link.send(req.toBytes()); } } catch (final ClosedChannelException e) { - logger.warn("Unable to send: " + cmd.toString()); + logger.warn("Unable to send: {}", cmd.toString()); } catch (final Exception e) { - logger.warn("Unable to send: " + cmd.toString() + " due to exception: ", e); + logger.warn("Unable to send: {} due to exception: {}", cmd.toString(), e); } logger.debug("Sending shutdown to management server"); try { @@ -332,111 +366,148 @@ public void stop(final String reason, final String detail) { } catch (final InterruptedException e) { logger.debug("Who the heck interrupted me here?"); } - _connection.stop(); - _connection = null; - _link = null; + connection.stop(); + connection = null; + link = null; } - if (_resource != null) { - _resource.stop(); - _resource = null; + if (serverResource != null) { + serverResource.stop(); + serverResource = null; } - if (_startup != null) { - _startup = null; + if (startupTask.get() != null) { + startupTask.set(null); } - if (_ugentTaskPool != null) { - _ugentTaskPool.shutdownNow(); - _ugentTaskPool = null; + if (outRequestHandler != null) { + outRequestHandler.shutdownNow(); + outRequestHandler = null; } - if (_executor != null) { - _executor.shutdown(); - _executor = null; + if (requestHandler != null) { + requestHandler.shutdown(); + requestHandler = null; } - if (_timer != null) { - _timer.cancel(); - _timer = null; + if (selfTaskExecutor != null) { + selfTaskExecutor.shutdown(); + selfTaskExecutor = null; } - if (hostLBTimer != null) { - hostLBTimer.cancel(); - hostLBTimer = null; + if (hostLbCheckExecutor != null) { + hostLbCheckExecutor.shutdown(); + hostLbCheckExecutor = null; } - if (certTimer != null) { - certTimer.cancel(); - certTimer = null; + if (certExecutor != null) { + certExecutor.shutdown(); + certExecutor = null; } } public Long getId() { - return _id; + return id; } public void setId(final Long id) { - logger.info("Set agent id " + id); - _id = id; - _shell.setPersistentProperty(getResourceName(), "id", Long.toString(id)); + logger.debug("Set agent id {}", id); + this.id = id; + shell.setPersistentProperty(getResourceName(), "id", Long.toString(id)); + } + + public String getUuid() { + return _uuid; } - private synchronized void scheduleServicesRestartTask() { - if (certTimer != null) { - certTimer.cancel(); - certTimer.purge(); + public void setUuid(String uuid) { + this._uuid = uuid; + shell.setPersistentProperty(getResourceName(), "uuid", uuid); + } + + public String getName() { + return _name; + } + + public void setName(String name) { + this._name = name; + shell.setPersistentProperty(getResourceName(), "name", name); + } + + private void scheduleCertificateRenewalTask() { + String name = "CertificateRenewalTask"; + if (certExecutor != null && !certExecutor.isShutdown()) { + certExecutor.shutdown(); + try { + if (!certExecutor.awaitTermination(1, TimeUnit.SECONDS)) { + certExecutor.shutdownNow(); + } + } catch (InterruptedException e) { + logger.debug("Forcing {} shutdown as it did not shutdown in the desired time due to: {}", + name, e.getMessage()); + certExecutor.shutdownNow(); + } } - certTimer = new Timer("Certificate Renewal Timer"); - certTimer.schedule(new PostCertificateRenewalTask(this), 5000L); + certExecutor = Executors.newSingleThreadScheduledExecutor((new NamedThreadFactory(name))); + certExecutor.schedule(new PostCertificateRenewalTask(this), 5, TimeUnit.SECONDS); } - private synchronized void scheduleHostLBCheckerTask(final long checkInterval) { - if (hostLBTimer != null) { - hostLBTimer.cancel(); + private void scheduleHostLBCheckerTask(final String lbAlgorithm, final long checkInterval) { + String name = "HostLBCheckerTask"; + if (hostLbCheckExecutor != null && !hostLbCheckExecutor.isShutdown()) { + logger.info("Shutting down the preferred host checker task {}", name); + hostLbCheckExecutor.shutdown(); + try { + if (!hostLbCheckExecutor.awaitTermination(1, TimeUnit.SECONDS)) { + hostLbCheckExecutor.shutdownNow(); + } + } catch (InterruptedException e) { + logger.debug("Forcing the preferred host checker task {} shutdown as it did not shutdown in the desired time due to: {}", + name, e.getMessage()); + hostLbCheckExecutor.shutdownNow(); + } } if (checkInterval > 0L) { - logger.info("Scheduling preferred host timer task with host.lb.interval=" + checkInterval + "ms"); - hostLBTimer = new Timer("Host LB Timer"); - hostLBTimer.scheduleAtFixedRate(new PreferredHostCheckerTask(), checkInterval, checkInterval); + if ("shuffle".equalsIgnoreCase(lbAlgorithm)) { + logger.info("Scheduling the preferred host checker task to trigger once (to apply lb algorithm '{}') after host.lb.interval={} ms", lbAlgorithm, checkInterval); + hostLbCheckExecutor = Executors.newSingleThreadScheduledExecutor((new NamedThreadFactory(name))); + hostLbCheckExecutor.schedule(new PreferredHostCheckerTask(), checkInterval, TimeUnit.MILLISECONDS); + return; + } + + logger.info("Scheduling a recurring preferred host checker task with host.lb.interval={} ms", checkInterval); + hostLbCheckExecutor = Executors.newSingleThreadScheduledExecutor((new NamedThreadFactory(name))); + hostLbCheckExecutor.scheduleAtFixedRate(new PreferredHostCheckerTask(), checkInterval, checkInterval, + TimeUnit.MILLISECONDS); } } public void scheduleWatch(final Link link, final Request request, final long delay, final long period) { - synchronized (_watchList) { - if (logger.isDebugEnabled()) { - logger.debug("Adding a watch list"); - } - final WatchTask task = new WatchTask(link, request, this); - _timer.schedule(task, 0, period); - _watchList.add(task); - } + logger.debug("Adding a watch list"); + final WatchTask task = new WatchTask(link, request, this); + final ScheduledFuture future = selfTaskExecutor.scheduleAtFixedRate(task, delay, period, TimeUnit.MILLISECONDS); + watchList.add(future); } public void triggerUpdate() { - PingCommand command = _resource.getCurrentStatus(getId()); + PingCommand command = serverResource.getCurrentStatus(getId()); command.setOutOfBand(true); logger.debug("Sending out of band ping"); - - final Request request = new Request(_id, -1, command, false); + final Request request = new Request(id, -1, command, false); request.setSequence(getNextSequence()); try { - _link.send(request.toBytes()); + link.send(request.toBytes()); } catch (final ClosedChannelException e) { - logger.warn("Unable to send ping update: " + request.toString()); + logger.warn("Unable to send ping update: {}", request.toString()); } } protected void cancelTasks() { - synchronized (_watchList) { - for (final WatchTask task : _watchList) { - task.cancel(); - } - if (logger.isDebugEnabled()) { - logger.debug("Clearing watch list: " + _watchList.size()); - } - _watchList.clear(); + for (final ScheduledFuture task : watchList) { + task.cancel(true); } + logger.debug("Clearing watch list: {}", () -> watchList.size()); + watchList.clear(); } /** @@ -447,61 +518,90 @@ protected void cancelTasks() { * when host is added back */ protected void cleanupAgentZoneProperties() { - _shell.setPersistentProperty(null, "zone", ""); - _shell.setPersistentProperty(null, "cluster", ""); - _shell.setPersistentProperty(null, "pod", ""); + shell.setPersistentProperty(null, "zone", ""); + shell.setPersistentProperty(null, "cluster", ""); + shell.setPersistentProperty(null, "pod", ""); } - public synchronized void lockStartupTask(final Link link) { - _startup = new StartupTask(link); - _timer.schedule(_startup, _startupWait); + public void lockStartupTask(final Link link) { + logger.debug("Creating startup task for link: {}", () -> getLinkLog(link)); + StartupTask currentTask = startupTask.get(); + if (currentTask != null) { + logger.warn("A Startup task is already locked or in progress, cannot create for link {}", + getLinkLog(link)); + return; + } + currentTask = new StartupTask(link); + if (startupTask.compareAndSet(null, currentTask)) { + selfTaskExecutor.schedule(currentTask, startupWait, TimeUnit.SECONDS); + return; + } + logger.warn("Failed to lock a StartupTask for link: {}", getLinkLog(link)); + } + + protected boolean cancelStartupTask() { + StartupTask task = startupTask.getAndSet(null); + if (task != null) { + task.cancel(); + return true; + } + return false; } public void sendStartup(final Link link) { - final StartupCommand[] startup = _resource.initialize(); + sendStartup(link, false); + } + + public void sendStartup(final Link link, boolean transfer) { + final StartupCommand[] startup = serverResource.initialize(); if (startup != null) { - final String msHostList = _shell.getPersistentProperty(null, "host"); + final String msHostList = shell.getPersistentProperty(null, "host"); final Command[] commands = new Command[startup.length]; for (int i = 0; i < startup.length; i++) { setupStartupCommand(startup[i]); startup[i].setMSHostList(msHostList); + startup[i].setConnectionTransferred(transfer); commands[i] = startup[i]; } - final Request request = new Request(_id != null ? _id : -1, -1, commands, false, false); + final Request request = new Request(id != null ? id : -1, -1, commands, false, false); request.setSequence(getNextSequence()); - if (logger.isDebugEnabled()) { - logger.debug("Sending Startup: " + request.toString()); - } + logger.debug("Sending Startup: {}", request.toString()); lockStartupTask(link); try { link.send(request.toBytes()); } catch (final ClosedChannelException e) { - logger.warn("Unable to send request: " + request.toString()); + logger.warn("Unable to send request to {} due to '{}', request: {}", + getLinkLog(link), e.getMessage(), request); } - if (_resource instanceof ResourceStatusUpdater) { - ((ResourceStatusUpdater) _resource).registerStatusUpdater(this); + if (serverResource instanceof ResourceStatusUpdater) { + ((ResourceStatusUpdater) serverResource).registerStatusUpdater(this); } } } - protected void setupStartupCommand(final StartupCommand startup) { - InetAddress addr; + protected String retrieveHostname() { + logger.trace("Retrieving hostname with resource={}", () -> serverResource.getClass().getSimpleName()); + final String result = Script.runSimpleBashScript(Script.getExecutableAbsolutePath("hostname"), 500); + if (StringUtils.isNotBlank(result)) { + return result; + } try { - addr = InetAddress.getLocalHost(); + InetAddress address = InetAddress.getLocalHost(); + return address.toString(); } catch (final UnknownHostException e) { logger.warn("unknown host? ", e); throw new CloudRuntimeException("Cannot get local IP address"); } + } - final Script command = new Script("hostname", 500, logger); - final OutputInterpreter.OneLineParser parser = new OutputInterpreter.OneLineParser(); - final String result = command.execute(parser); - final String hostname = result == null ? parser.getLine() : addr.toString(); - + protected void setupStartupCommand(final StartupCommand startup) { startup.setId(getId()); - if (startup.getName() == null) { + if (StringUtils.isBlank(startup.getName())) { + if (StringUtils.isBlank(hostname)) { + hostname = retrieveHostname(); + } startup.setName(hostname); } startup.setDataCenter(getZone()); @@ -509,6 +609,13 @@ protected void setupStartupCommand(final StartupCommand startup) { startup.setGuid(getResourceGuid()); startup.setResourceName(getResourceName()); startup.setVersion(getVersion()); + startup.setArch(getAgentArch()); + } + + protected String getAgentArch() { + String arch = Script.runSimpleBashScript(Script.getExecutableAbsolutePath("arch"), 2000); + logger.debug("Arch for agent: {} found: {}", _name, arch); + return arch; } @Override @@ -517,92 +624,104 @@ public Task create(final Task.Type type, final Link link, final byte[] data) { } protected void reconnect(final Link link) { - if (!_reconnectAllowed) { - return; - } - synchronized (this) { - if (_startup != null) { - _startup.cancel(); - _startup = null; - } - } + reconnect(link, null, false); + } - if (link != null) { - link.close(); - link.terminated(); + protected void reconnect(final Link link, String preferredMSHost, boolean forTransfer) { + if (!(forTransfer || reconnectAllowed)) { + logger.debug("Reconnect requested but it is not allowed {}", () -> getLinkLog(link)); + return; } - + cancelStartupTask(); + closeAndTerminateLink(link); + closeAndTerminateLink(this.link); setLink(null); cancelTasks(); + serverResource.disconnected(); + logger.info("Lost connection to host: {}. Attempting reconnection while we still have {} commands in progress.", shell.getConnectedHost(), commandsInProgress.get()); + stopAndCleanupConnection(true); + String host = preferredMSHost; + if (org.apache.commons.lang3.StringUtils.isBlank(host)) { + host = shell.getNextHost(); + } + List avoidMSHostList = shell.getAvoidHosts(); + do { + if (CollectionUtils.isEmpty(avoidMSHostList) || !avoidMSHostList.contains(host)) { + connection = new NioClient(getAgentName(), host, shell.getPort(), shell.getWorkers(), shell.getSslHandshakeTimeout(), this); + logger.info("Reconnecting to host: {}", host); + try { + connection.start(); + } catch (final NioConnectionException e) { + logger.info("Attempted to re-connect to the server, but received an unexpected exception, trying again...", e); + stopAndCleanupConnection(false); + } + } + shell.getBackoffAlgorithm().waitBeforeRetry(); + host = shell.getNextHost(); + } while (!connection.isStartup()); + shell.updateConnectedHost(((NioClient)connection).getHost()); + logger.info("Connected to the host: {}", shell.getConnectedHost()); + } - _resource.disconnected(); - - logger.info("Lost connection to host: " + _shell.getConnectedHost() + ". Attempting reconnection while we still have " + _inProgress.get() + " commands in progress."); - - _connection.stop(); + protected void closeAndTerminateLink(final Link link) { + if (link == null) { + return; + } + link.close(); + link.terminated(); + } + protected void stopAndCleanupConnection(boolean waitForStop) { + if (connection == null) { + return; + } + connection.stop(); try { - _connection.cleanUp(); + connection.cleanUp(); } catch (final IOException e) { - logger.warn("Fail to clean up old connection. " + e); + logger.warn("Fail to clean up old connection. {}", e); } - - while (_connection.isStartup()) { - _shell.getBackoffAlgorithm().waitBeforeRetry(); + if (!waitForStop) { + return; } - do { - final String host = _shell.getNextHost(); - _connection = new NioClient("Agent", host, _shell.getPort(), _shell.getWorkers(), this); - logger.info("Reconnecting to host:" + host); - try { - _connection.start(); - } catch (final NioConnectionException e) { - logger.info("Attempted to re-connect to the server, but received an unexpected exception, trying again...", e); - _connection.stop(); - try { - _connection.cleanUp(); - } catch (final IOException ex) { - logger.warn("Fail to clean up old connection. " + ex); - } - } - _shell.getBackoffAlgorithm().waitBeforeRetry(); - } while (!_connection.isStartup()); - _shell.updateConnectedHost(); - logger.info("Connected to the host: " + _shell.getConnectedHost()); + shell.getBackoffAlgorithm().waitBeforeRetry(); + } while (connection.isStartup()); } public void processStartupAnswer(final Answer answer, final Response response, final Link link) { - boolean cancelled = false; - synchronized (this) { - if (_startup != null) { - _startup.cancel(); - _startup = null; - } else { - cancelled = true; - } - } + boolean answerValid = cancelStartupTask(); final StartupAnswer startup = (StartupAnswer)answer; if (!startup.getResult()) { - logger.error("Not allowed to connect to the server: " + answer.getDetails()); + logger.error("Not allowed to connect to the server: {}", answer.getDetails()); + if (serverResource != null && !serverResource.isExitOnFailures()) { + logger.trace("{} does not allow exit on failure, reconnecting", + serverResource.getClass().getSimpleName()); + reconnect(link); + return; + } System.exit(1); } - if (cancelled) { + if (!answerValid) { logger.warn("Threw away a startup answer because we're reconnecting."); return; } - logger.info("Process agent startup answer, agent id = " + startup.getHostId()); + logger.info("Process agent startup answer, agent [id: {}, uuid: {}, name: {}] connected to the server", + startup.getHostId(), startup.getHostUuid(), startup.getHostName()); setId(startup.getHostId()); - _pingInterval = (long)startup.getPingInterval() * 1000; // change to ms. + setUuid(startup.getHostUuid()); + setName(startup.getHostName()); + pingInterval = startup.getPingInterval() * 1000L; // change to ms. - setLastPingResponseTime(); - scheduleWatch(link, response, _pingInterval, _pingInterval); + updateLastPingResponseTime(); + scheduleWatch(link, response, pingInterval, pingInterval); - _ugentTaskPool.setKeepAliveTime(2 * _pingInterval, TimeUnit.MILLISECONDS); + outRequestHandler.setKeepAliveTime(2 * pingInterval, TimeUnit.MILLISECONDS); - logger.info("Startup Response Received: agent id = " + getId()); + logger.info("Startup Response Received: agent [id: {}, uuid: {}, name: {}]", + startup.getHostId(), startup.getHostUuid(), startup.getHostName()); } protected void processRequest(final Request request, final Link link) { @@ -624,28 +743,35 @@ protected void processRequest(final Request request, final Link link) { { final String requestMsg = request.toString(); if (requestMsg != null) { - logger.debug("Request:" + requestMsg); + logger.debug("Request:{}",requestMsg); } requestLogged = true; } - logger.debug("Processing command: " + cmd.toString()); + logger.debug("Processing command: {}", cmd.toString()); } if (cmd instanceof CronCommand) { final CronCommand watch = (CronCommand)cmd; - scheduleWatch(link, request, (long)watch.getInterval() * 1000, watch.getInterval() * 1000); + scheduleWatch(link, request, watch.getInterval() * 1000L, watch.getInterval() * 1000L); answer = new Answer(cmd, true, null); } else if (cmd instanceof ShutdownCommand) { final ShutdownCommand shutdown = (ShutdownCommand)cmd; - logger.debug("Received shutdownCommand, due to: " + shutdown.getReason()); + logger.debug("Received shutdownCommand, due to: {}", shutdown.getReason()); cancelTasks(); if (shutdown.isRemoveHost()) { cleanupAgentZoneProperties(); } - _reconnectAllowed = false; + reconnectAllowed = false; answer = new Answer(cmd, true, null); } else if (cmd instanceof ReadyCommand && ((ReadyCommand)cmd).getDetails() != null) { - logger.debug("Not ready to connect to mgt server: " + ((ReadyCommand)cmd).getDetails()); + + logger.debug("Not ready to connect to mgt server: {}", ((ReadyCommand)cmd).getDetails()); + if (serverResource != null && !serverResource.isExitOnFailures()) { + logger.trace("{} does not allow exit on failure, reconnecting", + serverResource.getClass().getSimpleName()); + reconnect(link); + return; + } System.exit(1); return; } else if (cmd instanceof MaintainCommand) { @@ -653,40 +779,43 @@ protected void processRequest(final Request request, final Link link) { answer = new MaintainAnswer((MaintainCommand)cmd); } else if (cmd instanceof AgentControlCommand) { answer = null; - synchronized (_controlListeners) { - for (final IAgentControlListener listener : _controlListeners) { - answer = listener.processControlRequest(request, (AgentControlCommand)cmd); - if (answer != null) { - break; - } + for (final IAgentControlListener listener : controlListeners) { + answer = listener.processControlRequest(request, (AgentControlCommand)cmd); + if (answer != null) { + break; } } if (answer == null) { - logger.warn("No handler found to process cmd: " + cmd.toString()); + logger.warn("No handler found to process cmd: {}", cmd.toString()); answer = new AgentControlAnswer(cmd); } } else if (cmd instanceof SetupKeyStoreCommand && ((SetupKeyStoreCommand) cmd).isHandleByAgent()) { answer = setupAgentKeystore((SetupKeyStoreCommand) cmd); } else if (cmd instanceof SetupCertificateCommand && ((SetupCertificateCommand) cmd).isHandleByAgent()) { answer = setupAgentCertificate((SetupCertificateCommand) cmd); - if (Host.Type.Routing.equals(_resource.getType())) { - scheduleServicesRestartTask(); + if (Host.Type.Routing.equals(serverResource.getType())) { + scheduleCertificateRenewalTask(); } } else if (cmd instanceof SetupMSListCommand) { answer = setupManagementServerList((SetupMSListCommand) cmd); + } else if (cmd instanceof MigrateAgentConnectionCommand) { + answer = migrateAgentToOtherMS((MigrateAgentConnectionCommand) cmd); } else { if (cmd instanceof ReadyCommand) { processReadyCommand(cmd); } - _inProgress.incrementAndGet(); + commandsInProgress.incrementAndGet(); try { - answer = _resource.executeRequest(cmd); + if (cmd.isReconcile()) { + cmd.setRequestSequence(request.getSequence()); + } + answer = serverResource.executeRequest(cmd); } finally { - _inProgress.decrementAndGet(); + commandsInProgress.decrementAndGet(); } if (answer == null) { - logger.debug("Response: unsupported command" + cmd.toString()); + logger.debug("Response: unsupported command {}", cmd.toString()); answer = Answer.createUnsupportedCommandAnswer(cmd); } } @@ -718,7 +847,7 @@ protected void processRequest(final Request request, final Link link) { try { link.send(response.toBytes()); } catch (final ClosedChannelException e) { - logger.warn("Unable to send response: " + response.toString()); + logger.warn("Unable to send response: {}", response.toString()); } } } @@ -737,13 +866,13 @@ public Answer setupAgentKeystore(final SetupKeyStoreCommand cmd) { final String keyStoreFile = agentFile.getParent() + "/" + KeyStoreUtils.KS_FILENAME; final String csrFile = agentFile.getParent() + "/" + KeyStoreUtils.CSR_FILENAME; - String storedPassword = _shell.getPersistentProperty(null, KeyStoreUtils.KS_PASSPHRASE_PROPERTY); + String storedPassword = shell.getPersistentProperty(null, KeyStoreUtils.KS_PASSPHRASE_PROPERTY); if (StringUtils.isEmpty(storedPassword)) { storedPassword = keyStorePassword; - _shell.setPersistentProperty(null, KeyStoreUtils.KS_PASSPHRASE_PROPERTY, storedPassword); + shell.setPersistentProperty(null, KeyStoreUtils.KS_PASSPHRASE_PROPERTY, storedPassword); } - Script script = new Script(_keystoreSetupPath, 300000, logger); + Script script = new Script(keystoreSetupSetupPath, 300000, logger); script.add(agentFile.getAbsolutePath()); script.add(keyStoreFile); script.add(storedPassword); @@ -782,13 +911,13 @@ private Answer setupAgentCertificate(final SetupCertificateCommand cmd) { try { FileUtils.writeStringToFile(new File(certFile), certificate, Charset.defaultCharset()); FileUtils.writeStringToFile(new File(caCertFile), caCertificates, Charset.defaultCharset()); - logger.debug("Saved received client certificate to: " + certFile); + logger.debug("Saved received client certificate to: {}", certFile); } catch (IOException e) { throw new CloudRuntimeException("Unable to save received agent client and ca certificates", e); } - String ksPassphrase = _shell.getPersistentProperty(null, KeyStoreUtils.KS_PASSPHRASE_PROPERTY); - Script script = new Script(_keystoreCertImportPath, 300000, logger); + String ksPassphrase = shell.getPersistentProperty(null, KeyStoreUtils.KS_PASSPHRASE_PROPERTY); + Script script = new Script(keystoreCertImportScriptPath, 300000, logger); script.add(agentFile.getAbsolutePath()); script.add(ksPassphrase); script.add(keyStoreFile); @@ -806,50 +935,110 @@ private Answer setupAgentCertificate(final SetupCertificateCommand cmd) { return new SetupCertificateAnswer(true); } - private void processManagementServerList(final List msList, final String lbAlgorithm, final Long lbCheckInterval) { + private void processManagementServerList(final List msList, final List avoidMsList, final String lbAlgorithm, final Long lbCheckInterval, final boolean triggerHostLB) { if (CollectionUtils.isNotEmpty(msList) && StringUtils.isNotEmpty(lbAlgorithm)) { try { final String newMSHosts = String.format("%s%s%s", com.cloud.utils.StringUtils.toCSVList(msList), IAgentShell.hostLbAlgorithmSeparator, lbAlgorithm); - _shell.setPersistentProperty(null, "host", newMSHosts); - _shell.setHosts(newMSHosts); - _shell.resetHostCounter(); - logger.info("Processed new management server list: " + newMSHosts); + shell.setPersistentProperty(null, "host", newMSHosts); + shell.setHosts(newMSHosts); + shell.resetHostCounter(); + logger.info("Processed new management server list: {}", newMSHosts); } catch (final Exception e) { throw new CloudRuntimeException("Could not persist received management servers list", e); } } - if ("shuffle".equals(lbAlgorithm)) { - scheduleHostLBCheckerTask(0); - } else { - scheduleHostLBCheckerTask(_shell.getLbCheckerInterval(lbCheckInterval)); + shell.setAvoidHosts(avoidMsList); + if (triggerHostLB) { + logger.info("Triggering the preferred host checker task now"); + ScheduledExecutorService hostLbExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("HostLB-Executor")); + hostLbExecutor.schedule(new PreferredHostCheckerTask(), 0, TimeUnit.MILLISECONDS); + hostLbExecutor.shutdown(); } + scheduleHostLBCheckerTask(lbAlgorithm, shell.getLbCheckerInterval(lbCheckInterval)); } private Answer setupManagementServerList(final SetupMSListCommand cmd) { - processManagementServerList(cmd.getMsList(), cmd.getLbAlgorithm(), cmd.getLbCheckInterval()); + processManagementServerList(cmd.getMsList(), cmd.getAvoidMsList(), cmd.getLbAlgorithm(), cmd.getLbCheckInterval(), cmd.getTriggerHostLb()); return new SetupMSListAnswer(true); } + private Answer migrateAgentToOtherMS(final MigrateAgentConnectionCommand cmd) { + try { + if (CollectionUtils.isNotEmpty(cmd.getMsList())) { + processManagementServerList(cmd.getMsList(), cmd.getAvoidMsList(), cmd.getLbAlgorithm(), cmd.getLbCheckInterval(), false); + } + ScheduledExecutorService migrateAgentConnectionService = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("MigrateAgentConnection-Job")); + migrateAgentConnectionService.schedule(() -> { + migrateAgentConnection(cmd.getAvoidMsList()); + }, 3, TimeUnit.SECONDS); + migrateAgentConnectionService.shutdown(); + } catch (Exception e) { + String errMsg = "Migrate agent connection failed, due to " + e.getMessage(); + logger.debug(errMsg, e); + return new MigrateAgentConnectionAnswer(errMsg); + } + return new MigrateAgentConnectionAnswer(true); + } + + private void migrateAgentConnection(List avoidMsList) { + final String[] msHosts = shell.getHosts(); + if (msHosts == null || msHosts.length < 1) { + throw new CloudRuntimeException("Management Server hosts empty, not properly configured in agent"); + } + + List msHostsList = new ArrayList<>(Arrays.asList(msHosts)); + msHostsList.removeAll(avoidMsList); + if (msHostsList.isEmpty() || StringUtils.isEmpty(msHostsList.get(0))) { + throw new CloudRuntimeException("No other Management Server hosts to migrate"); + } + + String preferredMSHost = null; + for (String msHost : msHostsList) { + try (final Socket socket = new Socket()) { + socket.connect(new InetSocketAddress(msHost, shell.getPort()), 5000); + preferredMSHost = msHost; + break; + } catch (final IOException e) { + throw new CloudRuntimeException("Management server host: " + msHost + " is not reachable, to migrate connection"); + } + } + + if (preferredMSHost == null) { + throw new CloudRuntimeException("Management server host(s) are not reachable, to migrate connection"); + } + + logger.debug("Management server host " + preferredMSHost + " is found to be reachable, trying to reconnect"); + shell.resetHostCounter(); + shell.setAvoidHosts(avoidMsList); + shell.setConnectionTransfer(true); + reconnect(link, preferredMSHost, true); + } + public void processResponse(final Response response, final Link link) { final Answer answer = response.getAnswer(); - if (logger.isDebugEnabled()) { - logger.debug("Received response: " + response.toString()); - } + logger.debug("Received response: {}", response.toString()); if (answer instanceof StartupAnswer) { processStartupAnswer(answer, response, link); } else if (answer instanceof AgentControlAnswer) { // Notice, we are doing callback while holding a lock! - synchronized (_controlListeners) { - for (final IAgentControlListener listener : _controlListeners) { - listener.processControlResponse(response, (AgentControlAnswer)answer); - } + for (final IAgentControlListener listener : controlListeners) { + listener.processControlResponse(response, (AgentControlAnswer)answer); } - } else if (answer instanceof PingAnswer && (((PingAnswer) answer).isSendStartup()) && _reconnectAllowed) { + } else if (answer instanceof PingAnswer) { + processPingAnswer((PingAnswer) answer); + } else { + updateLastPingResponseTime(); + } + } + + private void processPingAnswer(final PingAnswer answer) { + if ((answer.isSendStartup()) && reconnectAllowed) { logger.info("Management server requested startup command to reinitialize the agent"); sendStartup(link); } else { - setLastPingResponseTime(); + serverResource.processPingAnswer((PingAnswer) answer); } + shell.setAvoidHosts(answer.getAvoidMsList()); } public void processReadyCommand(final Command cmd) { @@ -860,37 +1049,49 @@ public void processReadyCommand(final Command cmd) { NumbersUtil.enableHumanReadableSizes = humanReadable; } - logger.info("Processing agent ready command, agent id = " + ready.getHostId()); + logger.info("Processing agent ready command, agent id = {}, uuid = {}, name = {}", ready.getHostId(), ready.getHostUuid(), ready.getHostName()); if (ready.getHostId() != null) { setId(ready.getHostId()); + setUuid(ready.getHostUuid()); + setName(ready.getHostName()); } - processManagementServerList(ready.getMsHostList(), ready.getLbAlgorithm(), ready.getLbCheckInterval()); + verifyAgentArch(ready.getArch()); + processManagementServerList(ready.getMsHostList(), ready.getAvoidMsHostList(), ready.getLbAlgorithm(), ready.getLbCheckInterval(), false); + + logger.info("Ready command is processed for agent [id: {}, uuid: {}, name: {}]", getId(), getUuid(), getName()); + } - logger.info("Ready command is processed for agent id = " + getId()); + private void verifyAgentArch(String arch) { + if (StringUtils.isNotBlank(arch)) { + String agentArch = getAgentArch(); + if (!arch.equals(agentArch)) { + logger.error("Unexpected arch {}, expected {}", agentArch, arch); + } + } } public void processOtherTask(final Task task) { final Object obj = task.get(); if (obj instanceof Response) { - if (System.currentTimeMillis() - _lastPingResponseTime > _pingInterval * _shell.getPingRetries()) { - logger.error("Ping Interval has gone past " + _pingInterval * _shell.getPingRetries() + ". Won't reconnect to mgt server, as connection is still alive"); + if (System.currentTimeMillis() - lastPingResponseTime.get() > pingInterval * shell.getPingRetries()) { + logger.error("Ping Interval has gone past {}. Won't reconnect to mgt server, as connection is still alive", + pingInterval * shell.getPingRetries()); return; } - final PingCommand ping = _resource.getCurrentStatus(getId()); - final Request request = new Request(_id, -1, ping, false); + final PingCommand ping = serverResource.getCurrentStatus(getId()); + final Request request = new Request(id, -1, ping, false); request.setSequence(getNextSequence()); - if (logger.isDebugEnabled()) { - logger.debug("Sending ping: " + request.toString()); - } + logger.debug("Sending ping: {}", request.toString()); try { task.getLink().send(request.toBytes()); //if i can send pingcommand out, means the link is ok - setLastPingResponseTime(); + updateLastPingResponseTime(); } catch (final ClosedChannelException e) { - logger.warn("Unable to send request: " + request.toString()); + logger.warn("Unable to send request to {} due to '{}', request: {}", + getLinkLog(task.getLink()), e.getMessage(), request); } } else if (obj instanceof Request) { @@ -900,22 +1101,23 @@ public void processOtherTask(final Task task) { ThreadContext.put("logcontextid", command.getContextParam("logid")); } Answer answer = null; - _inProgress.incrementAndGet(); + commandsInProgress.incrementAndGet(); try { - answer = _resource.executeRequest(command); + if (command.isReconcile()) { + command.setRequestSequence(req.getSequence()); + } + answer = serverResource.executeRequest(command); } finally { - _inProgress.decrementAndGet(); + commandsInProgress.decrementAndGet(); } if (answer != null) { final Response response = new Response(req, answer); - if (logger.isDebugEnabled()) { - logger.debug("Watch Sent: " + response.toString()); - } + logger.debug("Watch Sent: {}", response.toString()); try { task.getLink().send(response.toBytes()); } catch (final ClosedChannelException e) { - logger.warn("Unable to send response: " + response.toString()); + logger.warn("Unable to send response: {}", response.toString()); } } } else { @@ -923,35 +1125,29 @@ public void processOtherTask(final Task task) { } } - public synchronized void setLastPingResponseTime() { - _lastPingResponseTime = System.currentTimeMillis(); + public void updateLastPingResponseTime() { + lastPingResponseTime.set(System.currentTimeMillis()); } - protected synchronized long getNextSequence() { - return _sequence++; + protected long getNextSequence() { + return sequence.getAndIncrement(); } @Override public void registerControlListener(final IAgentControlListener listener) { - synchronized (_controlListeners) { - _controlListeners.add(listener); - } + controlListeners.add(listener); } @Override public void unregisterControlListener(final IAgentControlListener listener) { - synchronized (_controlListeners) { - _controlListeners.remove(listener); - } + controlListeners.remove(listener); } @Override public AgentControlAnswer sendRequest(final AgentControlCommand cmd, final int timeoutInMilliseconds) throws AgentControlChannelException { final Request request = new Request(getId(), -1, new Command[] {cmd}, true, false); request.setSequence(getNextSequence()); - final AgentControlListener listener = new AgentControlListener(request); - registerControlListener(listener); try { postRequest(request); @@ -962,7 +1158,6 @@ public AgentControlAnswer sendRequest(final AgentControlCommand cmd, final int t logger.warn("sendRequest is interrupted, exit waiting"); } } - return listener.getAnswer(); } finally { unregisterControlListener(listener); @@ -977,11 +1172,11 @@ public void postRequest(final AgentControlCommand cmd) throws AgentControlChanne } private void postRequest(final Request request) throws AgentControlChannelException { - if (_link != null) { + if (link != null) { try { - _link.send(request.toBytes()); + link.send(request.toBytes()); } catch (final ClosedChannelException e) { - logger.warn("Unable to post agent control request: " + request.toString()); + logger.warn("Unable to post agent control request: {}", request.toString()); throw new AgentControlChannelException("Unable to post agent control request due to " + e.getMessage()); } } else { @@ -1031,28 +1226,26 @@ public void run() { } } - public class WatchTask extends ManagedContextTimerTask { + public class WatchTask implements Runnable { protected Request _request; protected Agent _agent; - protected Link _link; + protected Link link; public WatchTask(final Link link, final Request request, final Agent agent) { super(); _request = request; - _link = link; + this.link = link; _agent = agent; } @Override - protected void runInContext() { - if (logger.isTraceEnabled()) { - logger.trace("Scheduling " + (_request instanceof Response ? "Ping" : "Watch Task")); - } + public void run() { + logger.trace("Scheduling {}", (_request instanceof Response ? "Ping" : "Watch Task")); try { if (_request instanceof Response) { - _ugentTaskPool.submit(new ServerHandler(Task.Type.OTHER, _link, _request)); + outRequestHandler.submit(new ServerHandler(Task.Type.OTHER, link, _request)); } else { - _link.schedule(new ServerHandler(Task.Type.OTHER, _link, _request)); + link.schedule(new ServerHandler(Task.Type.OTHER, link, _request)); } } catch (final ClosedChannelException e) { logger.warn("Unable to schedule task because channel is closed"); @@ -1060,37 +1253,32 @@ protected void runInContext() { } } - public class StartupTask extends ManagedContextTimerTask { - protected Link _link; - protected volatile boolean cancelled = false; + public class StartupTask implements Runnable { + protected Link link; + private final AtomicBoolean cancelled = new AtomicBoolean(false); public StartupTask(final Link link) { logger.debug("Startup task created"); - _link = link; + this.link = link; } - @Override - public synchronized boolean cancel() { + public boolean cancel() { // TimerTask.cancel may fail depends on the calling context - if (!cancelled) { - cancelled = true; - _startupWait = _startupWaitDefault; + if (cancelled.compareAndSet(false, true)) { + startupWait = DEFAULT_STARTUP_WAIT; logger.debug("Startup task cancelled"); - return super.cancel(); } return true; } @Override - protected synchronized void runInContext() { - if (!cancelled) { - if (logger.isInfoEnabled()) { - logger.info("The startup command is now cancelled"); - } - cancelled = true; - _startup = null; - _startupWait = _startupWaitDefault * 2; - reconnect(_link); + public void run() { + if (cancelled.compareAndSet(false, true)) { + logger.info("The running startup command is now invalid. Attempting reconnect"); + startupTask.set(null); + startupWait = DEFAULT_STARTUP_WAIT * 2; + logger.debug("Executing reconnect from task - {}", () -> getLinkLog(link)); + reconnect(link); } } } @@ -1121,9 +1309,10 @@ public ServerHandler(final Task.Type type, final Link link, final Request req) { @Override public void doTask(final Task task) throws TaskExecutionException { if (task.getType() == Task.Type.CONNECT) { - _shell.getBackoffAlgorithm().reset(); + shell.getBackoffAlgorithm().reset(); setLink(task.getLink()); - sendStartup(task.getLink()); + sendStartup(task.getLink(), shell.isConnectionTransfer()); + shell.setConnectionTransfer(false); } else if (task.getType() == Task.Type.DATA) { Request request; try { @@ -1133,8 +1322,7 @@ 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()); - _executor.submit(new AgentRequestHandler(getType(), getLink(), request)); + requestHandler.submit(new AgentRequestHandler(getType(), getLink(), request)); } } catch (final ClassNotFoundException e) { logger.error("Unable to find this request "); @@ -1142,8 +1330,16 @@ public void doTask(final Task task) throws TaskExecutionException { logger.error("Error parsing task", e); } } else if (task.getType() == Task.Type.DISCONNECT) { + try { + // 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 - {} and reconnecting", () -> getLinkLog(task.getLink())); reconnect(task.getLink()); - return; } else if (task.getType() == Task.Type.OTHER) { processOtherTask(task); } @@ -1166,35 +1362,33 @@ public PostCertificateRenewalTask(final Agent agent) { protected void runInContext() { while (true) { try { - if (_inProgress.get() == 0) { + if (commandsInProgress.get() == 0) { logger.debug("Running post certificate renewal task to restart services."); // Let the resource perform any post certificate renewal cleanups - _resource.executeRequest(new PostCertificateRenewalCommand()); + serverResource.executeRequest(new PostCertificateRenewalCommand()); - IAgentShell shell = agent._shell; - ServerResource resource = agent._resource.getClass().newInstance(); + IAgentShell shell = agent.shell; + ServerResource resource = agent.serverResource.getClass().getDeclaredConstructor().newInstance(); // Stop current agent agent.cancelTasks(); - agent._reconnectAllowed = false; - Runtime.getRuntime().removeShutdownHook(agent._shutdownThread); + agent.reconnectAllowed = false; + Runtime.getRuntime().removeShutdownHook(agent.shutdownThread); agent.stop(ShutdownCommand.Requested, "Restarting due to new X509 certificates"); // Nullify references for GC - agent._shell = null; - agent._watchList = null; - agent._shutdownThread = null; - agent._controlListeners = null; + agent.shell = null; + agent.watchList = null; + agent.shutdownThread = null; + agent.controlListeners = null; agent = null; // Start a new agent instance shell.launchNewAgent(resource); return; } - if (logger.isTraceEnabled()) { - logger.debug("Other tasks are in progress, will retry post certificate renewal command after few seconds"); - } + logger.debug("Other tasks are in progress, will retry post certificate renewal command after few seconds"); Thread.sleep(5000); } catch (final Exception e) { logger.warn("Failed to execute post certificate renewal command:", e); @@ -1209,38 +1403,34 @@ public class PreferredHostCheckerTask extends ManagedContextTimerTask { @Override protected void runInContext() { try { - final String[] msList = _shell.getHosts(); + final String[] msList = shell.getHosts(); if (msList == null || msList.length < 1) { return; } - final String preferredHost = msList[0]; - final String connectedHost = _shell.getConnectedHost(); - if (logger.isTraceEnabled()) { - logger.trace("Running preferred host checker task, connected host=" + connectedHost + ", preferred host=" + preferredHost); + final String preferredMSHost = msList[0]; + final String connectedHost = shell.getConnectedHost(); + logger.debug("Running preferred host checker task, connected host={}, preferred host={}", + connectedHost, preferredMSHost); + if (preferredMSHost == null || preferredMSHost.equals(connectedHost) || link == null) { + return; } - if (preferredHost != null && !preferredHost.equals(connectedHost) && _link != null) { - boolean isHostUp = true; - try (final Socket socket = new Socket()) { - socket.connect(new InetSocketAddress(preferredHost, _shell.getPort()), 5000); - } catch (final IOException e) { - isHostUp = false; - if (logger.isTraceEnabled()) { - logger.trace("Host: " + preferredHost + " is not reachable"); - } - } - if (isHostUp && _link != null && _inProgress.get() == 0) { - if (logger.isDebugEnabled()) { - logger.debug("Preferred host " + preferredHost + " is found to be reachable, trying to reconnect"); - } - _shell.resetHostCounter(); - reconnect(_link); + boolean isHostUp = false; + try (final Socket socket = new Socket()) { + socket.connect(new InetSocketAddress(preferredMSHost, shell.getPort()), 5000); + isHostUp = true; + } catch (final IOException e) { + logger.debug("Host: {} is not reachable", preferredMSHost); + } + if (isHostUp && link != null && commandsInProgress.get() == 0) { + if (logger.isDebugEnabled()) { + logger.debug("Preferred host {} is found to be reachable, trying to reconnect", preferredMSHost); } + shell.resetHostCounter(); + reconnect(link, preferredMSHost, false); } } catch (Throwable t) { logger.error("Error caught while attempting to connect to preferred host", t); } } - } - } diff --git a/agent/src/main/java/com/cloud/agent/AgentShell.java b/agent/src/main/java/com/cloud/agent/AgentShell.java index 4b2bd9a524f6..4862e7e001e3 100644 --- a/agent/src/main/java/com/cloud/agent/AgentShell.java +++ b/agent/src/main/java/com/cloud/agent/AgentShell.java @@ -16,29 +16,6 @@ // under the License. package com.cloud.agent; -import com.cloud.agent.Agent.ExitStatus; -import com.cloud.agent.dao.StorageComponent; -import com.cloud.agent.dao.impl.PropertiesStorage; -import com.cloud.agent.properties.AgentProperties; -import com.cloud.agent.properties.AgentPropertiesFileHandler; -import com.cloud.resource.ServerResource; -import com.cloud.utils.LogUtils; -import com.cloud.utils.ProcessUtil; -import com.cloud.utils.PropertiesUtil; -import com.cloud.utils.backoff.BackoffAlgorithm; -import com.cloud.utils.backoff.impl.ConstantTimeBackoff; -import com.cloud.utils.exception.CloudRuntimeException; -import org.apache.commons.daemon.Daemon; -import org.apache.commons.daemon.DaemonContext; -import org.apache.commons.daemon.DaemonInitException; -import org.apache.commons.lang.math.NumberUtils; -import org.apache.commons.lang3.BooleanUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.core.config.Configurator; - -import javax.naming.ConfigurationException; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -53,6 +30,31 @@ import java.util.Properties; import java.util.UUID; +import javax.naming.ConfigurationException; + +import org.apache.commons.daemon.Daemon; +import org.apache.commons.daemon.DaemonContext; +import org.apache.commons.daemon.DaemonInitException; +import org.apache.commons.lang.math.NumberUtils; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.config.Configurator; + +import com.cloud.agent.Agent.ExitStatus; +import com.cloud.agent.dao.StorageComponent; +import com.cloud.agent.dao.impl.PropertiesStorage; +import com.cloud.agent.properties.AgentProperties; +import com.cloud.agent.properties.AgentPropertiesFileHandler; +import com.cloud.resource.ServerResource; +import com.cloud.utils.LogUtils; +import com.cloud.utils.ProcessUtil; +import com.cloud.utils.PropertiesUtil; +import com.cloud.utils.backoff.BackoffAlgorithm; +import com.cloud.utils.backoff.impl.ConstantTimeBackoff; +import com.cloud.utils.exception.CloudRuntimeException; + public class AgentShell implements IAgentShell, Daemon { protected static Logger LOGGER = LogManager.getLogger(AgentShell.class); @@ -64,6 +66,7 @@ public class AgentShell implements IAgentShell, Daemon { private String _zone; private String _pod; private String _host; + private List _avoidHosts; private String _privateIp; private int _port; private int _proxyPort; @@ -74,9 +77,9 @@ public class AgentShell implements IAgentShell, Daemon { private volatile boolean _exit = false; private int _pingRetries; private final List _agents = new ArrayList(); - private String hostToConnect; private String connectedHost; private Long preferredHostCheckInterval; + private boolean connectionTransfer = false; protected AgentProperties agentProperties = new AgentProperties(); public AgentShell() { @@ -118,7 +121,7 @@ public String getNextHost() { if (_hostCounter >= hosts.length) { _hostCounter = 0; } - hostToConnect = hosts[_hostCounter % hosts.length]; + String hostToConnect = hosts[_hostCounter % hosts.length]; _hostCounter++; return hostToConnect; } @@ -140,11 +143,10 @@ public long getLbCheckerInterval(final Long receivedLbInterval) { } @Override - public void updateConnectedHost() { - connectedHost = hostToConnect; + public void updateConnectedHost(String connectedHost) { + this.connectedHost = connectedHost; } - @Override public void resetHostCounter() { _hostCounter = 0; @@ -163,6 +165,16 @@ public void setHosts(final String host) { } } + @Override + public void setAvoidHosts(List avoidHosts) { + _avoidHosts = avoidHosts; + } + + @Override + public List getAvoidHosts() { + return _avoidHosts; + } + @Override public String getPrivateIp() { return _privateIp; @@ -215,6 +227,14 @@ public void setPersistentProperty(String prefix, String name, String value) { _storage.persist(name, value); } + public boolean isConnectionTransfer() { + return connectionTransfer; + } + + public void setConnectionTransfer(boolean connectionTransfer) { + this.connectionTransfer = connectionTransfer; + } + void loadProperties() throws ConfigurationException { final File file = PropertiesUtil.findConfigFile("agent.properties"); @@ -222,7 +242,7 @@ void loadProperties() throws ConfigurationException { throw new ConfigurationException("Unable to find agent.properties."); } - LOGGER.info("agent.properties found at " + file.getAbsolutePath()); + LOGGER.info("agent.properties found at {}", file.getAbsolutePath()); try { PropertiesUtil.loadFromFile(_properties, file); @@ -382,7 +402,7 @@ public void init(String[] args) throws ConfigurationException { if (_version == null) { throw new CloudRuntimeException("Unable to find the implementation version of this agent"); } - LOGGER.info("Implementation Version is " + _version); + LOGGER.info("Implementation Version is {}", _version); loadProperties(); parseCommand(args); @@ -390,7 +410,7 @@ public void init(String[] args) throws ConfigurationException { if (LOGGER.isDebugEnabled()) { List properties = Collections.list((Enumeration)_properties.propertyNames()); for (String property : properties) { - LOGGER.debug("Found property: " + property); + LOGGER.debug("Found property: {}", property); } } @@ -406,12 +426,14 @@ public void init(String[] args) throws ConfigurationException { LOGGER.info("Defaulting to the constant time backoff algorithm"); _backoff = new ConstantTimeBackoff(); - _backoff.configure("ConstantTimeBackoff", new HashMap()); + Map map = new HashMap<>(); + map.put("seconds", _properties.getProperty("backoff.seconds")); + _backoff.configure("ConstantTimeBackoff", map); } private void launchAgent() throws ConfigurationException { String resourceClassNames = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.RESOURCE); - LOGGER.trace("resource=" + resourceClassNames); + LOGGER.trace("resource={}", resourceClassNames); if (resourceClassNames != null) { launchAgentFromClassInfo(resourceClassNames); return; @@ -444,7 +466,7 @@ private void launchAgentFromTypeInfo() throws ConfigurationException { LOGGER.error("Unable to retrieve the type"); throw new ConfigurationException("Unable to retrieve the type of this agent."); } - LOGGER.trace("Launching agent based on type=" + typeInfo); + LOGGER.trace("Launching agent based on type={}", typeInfo); } public void launchNewAgent(ServerResource resource) throws ConfigurationException { @@ -455,6 +477,11 @@ public void launchNewAgent(ServerResource resource) throws ConfigurationExceptio agent.start(); } + @Override + public Integer getSslHandshakeTimeout() { + return AgentPropertiesFileHandler.getPropertyValue(AgentProperties.SSL_HANDSHAKE_TIMEOUT); + } + public synchronized int getNextAgentId() { return _nextAgentId++; } @@ -506,7 +533,7 @@ public void start() { String pidDir = getProperty(null, "piddir"); final String run = "agent." + instance + "pid"; - LOGGER.debug("Checking to see if " + run + " exists."); + LOGGER.debug("Checking to see if {} exists.", run); ProcessUtil.pidCheck(pidDir, run); launchAgent(); diff --git a/agent/src/main/java/com/cloud/agent/IAgentShell.java b/agent/src/main/java/com/cloud/agent/IAgentShell.java index 2dd08fffd459..9eefa6d2eeee 100644 --- a/agent/src/main/java/com/cloud/agent/IAgentShell.java +++ b/agent/src/main/java/com/cloud/agent/IAgentShell.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.agent; +import java.util.List; import java.util.Map; import java.util.Properties; @@ -63,11 +64,21 @@ public interface IAgentShell { String[] getHosts(); + void setAvoidHosts(List hosts); + + List getAvoidHosts(); + long getLbCheckerInterval(Long receivedLbInterval); - void updateConnectedHost(); + void updateConnectedHost(String connectedHost); String getConnectedHost(); void launchNewAgent(ServerResource resource) throws ConfigurationException; + + boolean isConnectionTransfer(); + + void setConnectionTransfer(boolean connectionTransfer); + + Integer getSslHandshakeTimeout(); } diff --git a/agent/src/main/java/com/cloud/agent/dao/impl/PropertiesStorage.java b/agent/src/main/java/com/cloud/agent/dao/impl/PropertiesStorage.java index b4b22fa8d168..17e0ceeeedea 100644 --- a/agent/src/main/java/com/cloud/agent/dao/impl/PropertiesStorage.java +++ b/agent/src/main/java/com/cloud/agent/dao/impl/PropertiesStorage.java @@ -93,14 +93,12 @@ public synchronized boolean configure(String name, Map params) { file = new File(path); try { if (!file.createNewFile()) { - logger.error(String.format("Unable to create _file: %s", file.getAbsolutePath())); + logger.error("Unable to create _file: {}", file.getAbsolutePath()); return false; } } catch (IOException e) { - logger.error(String.format("Unable to create file: %s", file.getAbsolutePath())); - if (logger.isDebugEnabled()) { - logger.debug(String.format("IOException while trying to create file: %s", file.getAbsolutePath()), e); - } + logger.error("Unable to create file: {}", file.getAbsolutePath()); + logger.debug("IOException while trying to create file: {}", file.getAbsolutePath(), e); return false; } } diff --git a/agent/src/main/java/com/cloud/agent/mockvm/MockVmMgr.java b/agent/src/main/java/com/cloud/agent/mockvm/MockVmMgr.java index 1e6fefa48180..54fdde3d3d28 100644 --- a/agent/src/main/java/com/cloud/agent/mockvm/MockVmMgr.java +++ b/agent/src/main/java/com/cloud/agent/mockvm/MockVmMgr.java @@ -87,8 +87,7 @@ public String startVM(String vmName, String vnetId, String gateway, String dns, @Override public String stopVM(String vmName, boolean force) { - if (logger.isInfoEnabled()) - logger.info("Stop VM. name: " + vmName); + logger.info("Stop VM. name: {}", vmName); synchronized (this) { MockVm vm = vms.get(vmName); @@ -103,8 +102,7 @@ public String stopVM(String vmName, boolean force) { @Override public String rebootVM(String vmName) { - if (logger.isInfoEnabled()) - logger.info("Reboot VM. name: " + vmName); + logger.info("Reboot VM. name: {}", vmName); synchronized (this) { MockVm vm = vms.get(vmName); @@ -116,8 +114,7 @@ public String rebootVM(String vmName) { @Override public boolean migrate(String vmName, String params) { - if (logger.isInfoEnabled()) - logger.info("Migrate VM. name: " + vmName); + logger.info("Migrate VM. name: {}", vmName); synchronized (this) { MockVm vm = vms.get(vmName); 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 322a0ed2444f..3364f9708cf5 100644 --- a/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java +++ b/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java @@ -117,7 +117,7 @@ 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/ */ @@ -134,7 +134,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 */ @@ -155,6 +155,14 @@ public class AgentProperties{ */ public static final Property CMDS_TIMEOUT = new Property<>("cmds.timeout", 7200); + /** + * The timeout (in seconds) for the snapshot merge operation, mainly used for classic volume snapshots and disk-only VM snapshots on file-based storage.
+ * This configuration is only considered if libvirt.events.enabled is also true.
+ * Data type: Integer.
+ * Default value: 259200 + */ + public static final Property QCOW2_DELTA_MERGE_TIMEOUT = new Property<>("qcow2.delta.merge.timeout", 60 * 60 * 72); + /** * This parameter sets the VM migration speed (in mbps). The default value is -1,
* which means that the agent will try to guess the speed of the guest network and consume all possible bandwidth.
@@ -213,6 +221,15 @@ public class AgentProperties{ */ public static final Property AGENT_HOOKS_LIBVIRT_VM_XML_TRANSFORMER_SCRIPT = new Property<>("agent.hooks.libvirt_vm_xml_transformer.script", "libvirt-vm-xml-transformer.groovy"); + /** + * This property is used with the agent.hooks.basedir property to define the Libvirt VM XML transformer shell script.
+ * The shell script is used to execute the Libvirt VM XML transformer script.
+ * For more information see the agent.properties file.
+ * Data type: String.
+ * Default value: libvirt-vm-xml-transformer.sh + */ + public static final Property AGENT_HOOKS_LIBVIRT_VM_XML_TRANSFORMER_SHELL_SCRIPT = new Property<>("agent.hooks.libvirt_vm_xml_transformer.shell_script", "libvirt-vm-xml-transformer.sh"); + /** * This property is used with the agent.hooks.basedir and agent.hooks.libvirt_vm_xml_transformer.script properties to define the Libvirt VM XML transformer method.
* Libvirt XML transformer hook does XML-to-XML transformation.
@@ -233,6 +250,15 @@ public class AgentProperties{ */ public static final Property AGENT_HOOKS_LIBVIRT_VM_ON_START_SCRIPT = new Property<>("agent.hooks.libvirt_vm_on_start.script", "libvirt-vm-state-change.groovy"); + /** + * This property is used with the agent.hooks.basedir property to define the Libvirt VM on start shell script.
+ * The shell script is used to execute the Libvirt VM on start script.
+ * For more information see the agent.properties file.
+ * Data type: String.
+ * Default value: libvirt-vm-state-change.sh + */ + public static final Property AGENT_HOOKS_LIBVIRT_VM_ON_START_SHELL_SCRIPT = new Property<>("agent.hooks.libvirt_vm_on_start.shell_script", "libvirt-vm-state-change.sh"); + /** * This property is used with the agent.hooks.basedir and agent.hooks.libvirt_vm_on_start.script properties to define the Libvirt VM on start method.
* The hook is called right after Libvirt successfully launched the VM.
@@ -252,6 +278,15 @@ public class AgentProperties{ */ public static final Property AGENT_HOOKS_LIBVIRT_VM_ON_STOP_SCRIPT = new Property<>("agent.hooks.libvirt_vm_on_stop.script", "libvirt-vm-state-change.groovy"); + /** + * This property is used with the agent.hooks.basedir property to define the Libvirt VM on stop shell script.
+ * The shell script is used to execute the Libvirt VM on stop script.
+ * For more information see the agent.properties file.
+ * Data type: String.
+ * Default value: libvirt-vm-state-change.sh + */ + public static final Property AGENT_HOOKS_LIBVIRT_VM_ON_STOP_SHELL_SCRIPT = new Property<>("agent.hooks.libvirt_vm_on_stop.shell_script", "libvirt-vm-state-change.sh"); + /** * This property is used with the agent.hooks.basedir and agent.hooks.libvirt_vm_on_stop.script properties to define the Libvirt VM on stop method.
* The hook is called right after libvirt successfully stopped the VM.
@@ -383,15 +418,16 @@ public class AgentProperties{ /** * This param will set the CPU architecture for the domain to override what the management server would send.
* In case of arm64 (aarch64), this will change the machine type to 'virt' and add a SCSI and a USB controller in the domain XML.
- * Possible values: x86_64 | aarch64
+ * Possible values: x86_64 | aarch64 | s390x
* Data type: String.
* Default value: null (will set use the architecture of the VM's OS). */ public static final Property GUEST_CPU_ARCH = new Property<>("guest.cpu.arch", null, String.class); /** - * This param will require CPU features on the CPU section.
- * The features listed in this property must be separated by a blank space (see example below).
+ * Specifies required CPU features for end-user and system VMs.
+ * These features must be present on the host CPU for VM deployment.
+ * Multiple features should be separated by whitespace (see example below).
* Possible values: vmx vme
* Data type: String.
* Default value: null @@ -516,6 +552,7 @@ public class AgentProperties{ /** * The model of Watchdog timer to present to the Guest.
* For all models refer to the libvirt documentation.
+ * PLEASE NOTE: to disable the watchdogs definitions, use value: none * Data type: String.
* Default value: i6300esb */ @@ -695,6 +732,13 @@ public class AgentProperties{ */ public static final Property DEVELOPER = new Property<>("developer", false); + /** + * If set to "true", the agent will register for libvirt domain events, allowing for immediate updates on crashed or unexpectedly + * stopped VMs. Experimental, requires agent restart. + * Default value: false + */ + public static final Property LIBVIRT_EVENTS_ENABLED = new Property<>("libvirt.events.enabled", false); + /** * Can only be used if developer = true. This property is used to define the local bridge name and private network name.
* Data type: String.
@@ -744,12 +788,26 @@ public Property getWorkers() { public static final Property IOTHREADS = new Property<>("iothreads", 1); /** - * Enable verbose mode for virt-v2v Instance Conversion from Vmware to KVM + * Enable verbose mode for virt-v2v Instance Conversion from VMware to KVM * Data type: Boolean.
* Default value: false */ 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); + /** * BGP controll CIDR * Data type: String.
@@ -796,12 +854,44 @@ public Property getWorkers() { */ public static final Property KEYSTORE_PASSPHRASE = new Property<>(KeyStoreUtils.KS_PASSPHRASE_PROPERTY, null, String.class); + /** + * Implicit host tags + * Data type: String.
+ * Default value: null + */ + public static final Property HOST_TAGS = new Property<>("host.tags", null, String.class); + + /** + * Timeout for SSL handshake in seconds + * Data type: Integer.
+ * Default value: null + */ + public static final Property SSL_HANDSHAKE_TIMEOUT = new Property<>("ssl.handshake.timeout", 30, Integer.class); + + /** + * Timeout (in seconds) to wait for the incremental snapshot to complete. + * */ + public static final Property INCREMENTAL_SNAPSHOT_TIMEOUT = new Property<>("incremental.snapshot.timeout", 10800); + + /** + * Timeout (in seconds) to wait for the snapshot reversion to complete. + * */ + public static final Property REVERT_SNAPSHOT_TIMEOUT = new Property<>("revert.snapshot.timeout", 10800); + + /** + * If set to true, creates VMs as full clones of their templates on KVM hypervisor. Creates as linked clones otherwise.
+ * Data type: Boolean.
+ * Default value: false + */ + public static final Property CREATE_FULL_CLONE = new Property<>("create.full.clone", false); + + public static class Property { private String name; private T defaultValue; private Class typeClass; - Property(String name, T value) { + public Property(String name, T value) { init(name, value); } diff --git a/agent/src/main/java/com/cloud/agent/properties/AgentPropertiesFileHandler.java b/agent/src/main/java/com/cloud/agent/properties/AgentPropertiesFileHandler.java index 614848fb96e4..b28018fcd943 100644 --- a/agent/src/main/java/com/cloud/agent/properties/AgentPropertiesFileHandler.java +++ b/agent/src/main/java/com/cloud/agent/properties/AgentPropertiesFileHandler.java @@ -48,7 +48,7 @@ public static T getPropertyValue(AgentProperties.Property property) { File agentPropertiesFile = PropertiesUtil.findConfigFile(KeyStoreUtils.AGENT_PROPSFILE); if (agentPropertiesFile == null) { - LOGGER.debug(String.format("File [%s] was not found, we will use default defined values. Property [%s]: [%s].", KeyStoreUtils.AGENT_PROPSFILE, name, defaultValue)); + LOGGER.debug("File [{}] was not found, we will use default defined values. Property [{}]: [{}].", KeyStoreUtils.AGENT_PROPSFILE, name, defaultValue); return defaultValue; } @@ -56,7 +56,7 @@ public static T getPropertyValue(AgentProperties.Property property) { try { String configValue = PropertiesUtil.loadFromFile(agentPropertiesFile).getProperty(name); if (StringUtils.isBlank(configValue)) { - LOGGER.debug(String.format("Property [%s] has empty or null value. Using default value [%s].", name, defaultValue)); + LOGGER.debug("Property [{}] has empty or null value. Using default value [{}].", name, defaultValue); return defaultValue; } @@ -68,11 +68,11 @@ public static T getPropertyValue(AgentProperties.Property property) { ConvertUtils.register(new LongConverter(defaultValue), Long.class); } - LOGGER.debug(String.format("Property [%s] was altered. Now using the value [%s].", name, configValue)); + LOGGER.debug("Property [{}] was altered. Now using the value [{}].", name, configValue); return (T)ConvertUtils.convert(configValue, property.getTypeClass()); } catch (IOException ex) { - LOGGER.debug(String.format("Failed to get property [%s]. Using default value [%s].", name, defaultValue), ex); + LOGGER.debug("Failed to get property [{}]. Using default value [{}].", name, defaultValue, ex); } return defaultValue; diff --git a/agent/src/main/java/com/cloud/agent/resource/DummyResource.java b/agent/src/main/java/com/cloud/agent/resource/DummyResource.java index fe519ca9497f..4002e53b5858 100644 --- a/agent/src/main/java/com/cloud/agent/resource/DummyResource.java +++ b/agent/src/main/java/com/cloud/agent/resource/DummyResource.java @@ -20,7 +20,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.UUID; import com.cloud.agent.IAgentControl; import com.cloud.agent.api.Answer; @@ -40,6 +39,7 @@ import com.cloud.storage.Storage; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.utils.StringUtils; +import com.cloud.utils.UuidUtils; public class DummyResource implements ServerResource { String _name; @@ -133,7 +133,7 @@ protected StoragePoolInfo initializeLocalStorage() { String hostIp = getConfiguredProperty("private.ip.address", "127.0.0.1"); String localStoragePath = getConfiguredProperty("local.storage.path", "/mnt"); String lh = hostIp + localStoragePath; - String uuid = UUID.nameUUIDFromBytes(lh.getBytes(StringUtils.getPreferredCharset())).toString(); + String uuid = UuidUtils.nameUUIDFromBytes(lh.getBytes(StringUtils.getPreferredCharset())).toString(); String capacity = getConfiguredProperty("local.storage.capacity", "1000000000"); String available = getConfiguredProperty("local.storage.avail", "10000000"); diff --git a/agent/src/main/java/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java b/agent/src/main/java/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java index f0407a129882..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; } @@ -278,20 +278,19 @@ public boolean configure(String name, Map params) throws Configu disableRpFilter(); } - if (logger.isInfoEnabled()) - logger.info("Receive proxyVmId in ConsoleProxyResource configuration as " + proxyVmId); + logger.info("Receive proxyVmId in ConsoleProxyResource configuration as {}", proxyVmId); return true; } private void addRouteToInternalIpOrCidr(String localgw, String eth1ip, String eth1mask, String destIpOrCidr) { - logger.debug("addRouteToInternalIp: localgw=" + localgw + ", eth1ip=" + eth1ip + ", eth1mask=" + eth1mask + ",destIp=" + destIpOrCidr); + logger.debug("addRouteToInternalIp: localgw={}, eth1ip={}, eth1mask={}, destIp={}", localgw, eth1ip, eth1mask, destIpOrCidr); if (destIpOrCidr == null) { logger.debug("addRouteToInternalIp: destIp is null"); return; } if (!NetUtils.isValidIp4(destIpOrCidr) && !NetUtils.isValidIp4Cidr(destIpOrCidr)) { - logger.warn(" destIp is not a valid ip address or cidr destIp=" + destIpOrCidr); + logger.warn(" destIp is not a valid ip address or cidr destIp={}", destIpOrCidr); return; } boolean inSameSubnet = false; @@ -299,13 +298,13 @@ private void addRouteToInternalIpOrCidr(String localgw, String eth1ip, String et if (eth1ip != null && eth1mask != null) { inSameSubnet = NetUtils.sameSubnet(eth1ip, destIpOrCidr, eth1mask); } else { - logger.warn("addRouteToInternalIp: unable to determine same subnet: eth1ip=" + eth1ip + ", dest ip=" + destIpOrCidr + ", eth1mask=" + eth1mask); + logger.warn("addRouteToInternalIp: unable to determine same subnet: eth1ip={}, dest ip={}, eth1mask={}", eth1ip, destIpOrCidr, eth1mask); } } else { inSameSubnet = NetUtils.isNetworkAWithinNetworkB(destIpOrCidr, NetUtils.ipAndNetMaskToCidr(eth1ip, eth1mask)); } if (inSameSubnet) { - logger.debug("addRouteToInternalIp: dest ip " + destIpOrCidr + " is in the same subnet as eth1 ip " + eth1ip); + logger.debug("addRouteToInternalIp: dest ip {} is in the same subnet as eth1 ip {}", destIpOrCidr, eth1ip); return; } Script command = new Script("/bin/bash", logger); @@ -317,9 +316,9 @@ private void addRouteToInternalIpOrCidr(String localgw, String eth1ip, String et command.add("ip route add " + destIpOrCidr + " via " + localgw); String result = command.execute(); if (result != null) { - logger.warn("Error in configuring route to internal ip err=" + result); + logger.warn("Error in configuring route to internal ip err={}", result); } else { - logger.debug("addRouteToInternalIp: added route to internal ip=" + destIpOrCidr + " via " + localgw); + logger.debug("addRouteToInternalIp: added route to internal ip={} via {}", destIpOrCidr, localgw); } } @@ -332,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() { @@ -355,7 +354,7 @@ protected void runInContext() { logger.error("Unable to launch console proxy due to IllegalAccessException", e); System.exit(ExitStatus.Error.value()); } catch (InvocationTargetException e) { - logger.error("Unable to launch console proxy due to InvocationTargetException " + e.getTargetException().toString(), e); + logger.error("Unable to launch console proxy due to InvocationTargetException {}", e.getTargetException().toString(), e); System.exit(ExitStatus.Error.value()); } } catch (final ClassNotFoundException e) { @@ -398,9 +397,8 @@ protected void runInContext() { } public String authenticateConsoleAccess(String host, String port, String vmId, String sid, String ticket, - Boolean isReauthentication, String sessionToken) { - - ConsoleAccessAuthenticationCommand cmd = new ConsoleAccessAuthenticationCommand(host, port, vmId, sid, ticket, sessionToken); + Boolean isReauthentication, String sessionToken, String clientAddress) { + ConsoleAccessAuthenticationCommand cmd = new ConsoleAccessAuthenticationCommand(host, port, vmId, sid, ticket, sessionToken, clientAddress); cmd.setReauthenticating(isReauthentication); ConsoleProxyAuthenticationResult result = new ConsoleProxyAuthenticationResult(); @@ -418,10 +416,10 @@ public String authenticateConsoleAccess(String host, String port, String vmId, S result.setTunnelUrl(authAnswer.getTunnelUrl()); result.setTunnelSession(authAnswer.getTunnelSession()); } else { - logger.error("Authentication failed for vm: " + vmId + " with sid: " + sid); + logger.error("Authentication failed for vm: {} with sid: {}", vmId, sid); } } catch (AgentControlChannelException e) { - logger.error("Unable to send out console access authentication request due to " + e.getMessage(), e); + logger.error("Unable to send out console access authentication request due to {}", e.getMessage(), e); } return new Gson().toJson(result); @@ -431,18 +429,15 @@ public void reportLoadInfo(String gsonLoadInfo) { ConsoleProxyLoadReportCommand cmd = new ConsoleProxyLoadReportCommand(proxyVmId, gsonLoadInfo); try { getAgentControl().postRequest(cmd); - - if (logger.isDebugEnabled()) - logger.debug("Report proxy load info, proxy : " + proxyVmId + ", load: " + gsonLoadInfo); + logger.debug("Report proxy load info, proxy : {}, load: {}", proxyVmId, gsonLoadInfo); } catch (AgentControlChannelException e) { - logger.error("Unable to send out load info due to " + e.getMessage(), e); + logger.error("Unable to send out load info due to {}", e.getMessage(), e); } } public void ensureRoute(String address) { if (localGateway != null) { - if (logger.isDebugEnabled()) - logger.debug("Ensure route for " + address + " via " + localGateway); + logger.debug("Ensure route for {} via {}", address, localGateway); // this method won't be called in high frequency, serialize access // to script execution @@ -450,7 +445,7 @@ public void ensureRoute(String address) { try { addRouteToInternalIpOrCidr(localGateway, eth1Ip, eth1Mask, address); } catch (Throwable e) { - logger.warn("Unexpected exception while adding internal route to " + address, e); + logger.warn("Unexpected exception while adding internal route to {}", address, e); } } } diff --git a/agent/src/test/java/com/cloud/agent/AgentShellTest.java b/agent/src/test/java/com/cloud/agent/AgentShellTest.java index f7151779f585..d8def24a603a 100644 --- a/agent/src/test/java/com/cloud/agent/AgentShellTest.java +++ b/agent/src/test/java/com/cloud/agent/AgentShellTest.java @@ -350,4 +350,23 @@ public void setHostTestValueIsNullPropertyDoesNotStartAndEndWithAtSignSetHosts() Mockito.verify(agentShellSpy).setHosts(expected); } + + @Test + public void updateAndGetConnectedHost() { + String expected = "test"; + + AgentShell shell = new AgentShell(); + shell.setHosts("test"); + shell.getNextHost(); + shell.updateConnectedHost("test"); + + Assert.assertEquals(expected, shell.getConnectedHost()); + } + + @Test + public void testGetSslHandshakeTimeout() { + Integer expected = 1; + agentPropertiesFileHandlerMocked.when(() -> AgentPropertiesFileHandler.getPropertyValue(Mockito.eq(AgentProperties.SSL_HANDSHAKE_TIMEOUT))).thenReturn(expected); + Assert.assertEquals(expected, agentShellSpy.getSslHandshakeTimeout()); + } } diff --git a/agent/src/test/java/com/cloud/agent/AgentTest.java b/agent/src/test/java/com/cloud/agent/AgentTest.java new file mode 100644 index 000000000000..65dc030ebd76 --- /dev/null +++ b/agent/src/test/java/com/cloud/agent/AgentTest.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 com.cloud.agent; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.net.InetSocketAddress; + +import javax.naming.ConfigurationException; + +import org.apache.logging.log4j.Logger; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import com.cloud.resource.ServerResource; +import com.cloud.utils.backoff.impl.ConstantTimeBackoff; +import com.cloud.utils.nio.Link; +import com.cloud.utils.nio.NioConnection; + +@RunWith(MockitoJUnitRunner.class) +public class AgentTest { + Agent agent; + private AgentShell shell; + private ServerResource serverResource; + private Logger logger; + + @Before + public void setUp() throws ConfigurationException { + shell = mock(AgentShell.class); + serverResource = mock(ServerResource.class); + doReturn(true).when(serverResource).configure(any(), any()); + doReturn(1).when(shell).getWorkers(); + doReturn(1).when(shell).getPingRetries(); + agent = new Agent(shell, 1, serverResource); + logger = mock(Logger.class); + ReflectionTestUtils.setField(agent, "logger", logger); + } + + @Test + public void testGetLinkLogNullLinkReturnsEmptyString() { + Link link = null; + String result = agent.getLinkLog(link); + assertEquals("", result); + } + + @Test + public void testGetLinkLogLinkWithTraceEnabledReturnsLinkLogWithHashCode() { + Link link = mock(Link.class); + InetSocketAddress socketAddress = new InetSocketAddress("192.168.1.100", 1111); + when(link.getSocketAddress()).thenReturn(socketAddress); + when(logger.isTraceEnabled()).thenReturn(true); + + String result = agent.getLinkLog(link); + System.out.println(result); + assertTrue(result.startsWith(System.identityHashCode(link) + "-")); + assertTrue(result.contains("192.168.1.100")); + } + + @Test + public void testGetAgentNameWhenServerResourceIsNull() { + ReflectionTestUtils.setField(agent, "serverResource", null); + assertEquals("Agent", agent.getAgentName()); + } + + @Test + public void testGetAgentNameWhenAppendAgentNameIsTrue() { + when(serverResource.isAppendAgentNameToLogs()).thenReturn(true); + when(serverResource.getName()).thenReturn("TestAgent"); + + String agentName = agent.getAgentName(); + assertEquals("TestAgent", agentName); + } + + @Test + public void testGetAgentNameWhenAppendAgentNameIsFalse() { + when(serverResource.isAppendAgentNameToLogs()).thenReturn(false); + + String agentName = agent.getAgentName(); + assertEquals("Agent", agentName); + } + + @Test + public void testAgentInitialization() { + Runtime.getRuntime().removeShutdownHook(agent.shutdownThread); + when(shell.getPingRetries()).thenReturn(3); + when(shell.getWorkers()).thenReturn(5); + agent.setupShutdownHookAndInitExecutors(); + assertNotNull(agent.selfTaskExecutor); + assertNotNull(agent.outRequestHandler); + assertNotNull(agent.requestHandler); + } + + @Test + public void testAgentShutdownHookAdded() { + Runtime.getRuntime().removeShutdownHook(agent.shutdownThread); + agent.setupShutdownHookAndInitExecutors(); + verify(logger).trace("Adding shutdown hook"); + } + + @Test + public void testGetResourceGuidValidGuidAndResourceName() { + when(shell.getGuid()).thenReturn("12345"); + String result = agent.getResourceGuid(); + assertTrue(result.startsWith("12345-" + ServerResource.class.getSimpleName())); + } + + @Test + public void testGetZoneReturnsValidZone() { + when(shell.getZone()).thenReturn("ZoneA"); + String result = agent.getZone(); + assertEquals("ZoneA", result); + } + + @Test + public void testGetPodReturnsValidPod() { + when(shell.getPod()).thenReturn("PodA"); + String result = agent.getPod(); + assertEquals("PodA", result); + } + + @Test + public void testSetLinkAssignsLink() { + Link mockLink = mock(Link.class); + agent.setLink(mockLink); + assertEquals(mockLink, agent.link); + } + + @Test + public void testGetResourceReturnsServerResource() { + ServerResource mockResource = mock(ServerResource.class); + ReflectionTestUtils.setField(agent, "serverResource", mockResource); + ServerResource result = agent.getResource(); + assertSame(mockResource, result); + } + + @Test + public void testGetResourceName() { + String result = agent.getResourceName(); + assertTrue(result.startsWith(ServerResource.class.getSimpleName())); + } + + @Test + public void testUpdateLastPingResponseTimeUpdatesCurrentTime() { + long beforeUpdate = System.currentTimeMillis(); + agent.updateLastPingResponseTime(); + long updatedTime = agent.lastPingResponseTime.get(); + assertTrue(updatedTime >= beforeUpdate); + assertTrue(updatedTime <= System.currentTimeMillis()); + } + + @Test + public void testGetNextSequenceIncrementsSequence() { + long initialSequence = agent.getNextSequence(); + long nextSequence = agent.getNextSequence(); + assertEquals(initialSequence + 1, nextSequence); + long thirdSequence = agent.getNextSequence(); + assertEquals(nextSequence + 1, thirdSequence); + } + + @Test + public void testRegisterControlListenerAddsListener() { + IAgentControlListener listener = mock(IAgentControlListener.class); + agent.registerControlListener(listener); + assertTrue(agent.controlListeners.contains(listener)); + } + + @Test + public void testUnregisterControlListenerRemovesListener() { + IAgentControlListener listener = mock(IAgentControlListener.class); + agent.registerControlListener(listener); + assertTrue(agent.controlListeners.contains(listener)); + agent.unregisterControlListener(listener); + assertFalse(agent.controlListeners.contains(listener)); + } + + @Test + public void testCloseAndTerminateLinkLinkIsNullDoesNothing() { + agent.closeAndTerminateLink(null); + } + + @Test + public void testCloseAndTerminateLinkValidLinkCallsCloseAndTerminate() { + Link mockLink = mock(Link.class); + agent.closeAndTerminateLink(mockLink); + verify(mockLink).close(); + verify(mockLink).terminated(); + } + + @Test + public void testStopAndCleanupConnectionConnectionIsNullDoesNothing() { + agent.connection = null; + agent.stopAndCleanupConnection(false); + } + + @Test + public void testStopAndCleanupConnectionValidConnectionNoWaitStopsAndCleansUp() throws IOException { + NioConnection mockConnection = mock(NioConnection.class); + agent.connection = mockConnection; + agent.stopAndCleanupConnection(false); + verify(mockConnection).stop(); + verify(mockConnection).cleanUp(); + } + + @Test + public void testStopAndCleanupConnectionCleanupThrowsIOExceptionLogsWarning() throws IOException { + NioConnection mockConnection = mock(NioConnection.class); + agent.connection = mockConnection; + doThrow(new IOException("Cleanup failed")).when(mockConnection).cleanUp(); + agent.stopAndCleanupConnection(false); + verify(mockConnection).stop(); + verify(logger).warn(eq("Fail to clean up old connection. {}"), any(IOException.class)); + } + + @Test + public void testStopAndCleanupConnectionValidConnectionWaitForStopWaitsForStartupToStop() throws IOException { + NioConnection mockConnection = mock(NioConnection.class); + ConstantTimeBackoff mockBackoff = mock(ConstantTimeBackoff.class); + mockBackoff.setTimeToWait(0); + agent.connection = mockConnection; + when(shell.getBackoffAlgorithm()).thenReturn(mockBackoff); + when(mockConnection.isStartup()).thenReturn(true, true, false); + agent.stopAndCleanupConnection(true); + verify(mockConnection).stop(); + verify(mockConnection).cleanUp(); + verify(mockBackoff, times(3)).waitBeforeRetry(); + } +} diff --git a/api/pom.xml b/api/pom.xml index 32897725e0c1..c80c35593451 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.0.0-SNAPSHOT + 4.23.0.0-SNAPSHOT diff --git a/api/src/main/java/com/cloud/agent/api/Command.java b/api/src/main/java/com/cloud/agent/api/Command.java index eb979c0060b9..c4e99cb41707 100644 --- a/api/src/main/java/com/cloud/agent/api/Command.java +++ b/api/src/main/java/com/cloud/agent/api/Command.java @@ -19,9 +19,10 @@ import java.util.HashMap; import java.util.Map; -import com.cloud.agent.api.LogLevel.Log4jLevel; -import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import com.cloud.agent.api.LogLevel.Log4jLevel; /** * implemented by classes that extends the Command class. Command specifies @@ -35,6 +36,23 @@ public static enum OnError { Continue, Stop } + public enum State { + CREATED, // Command is created by management server + STARTED, // Command is started by agent + PROCESSING, // Processing by agent + PROCESSING_IN_BACKEND, // Processing in backend by agent + COMPLETED, // Operation succeeds by agent or management server + FAILED, // Operation fails by agent + RECONCILE_RETRY, // Ready for retry of reconciliation + RECONCILING, // Being reconciled by management server + RECONCILED, // Reconciled by management server + RECONCILE_SKIPPED, // Skip the reconciliation as the resource state is inconsistent with the command + RECONCILE_FAILED, // Fail to reconcile by management server + TIMED_OUT, // Timed out on management server or agent + INTERRUPTED, // Interrupted by management server or agent (for example agent is restarted), + DANGLED_IN_BACKEND // Backend process which cannot be processed normally (for example agent is restarted) + } + public static final String HYPERVISOR_TYPE = "hypervisorType"; // allow command to carry over hypervisor or other environment related context info @@ -42,6 +60,8 @@ public static enum OnError { protected Map contextMap = new HashMap(); private int wait; //in second private boolean bypassHostMaintenance = false; + private transient long requestSequence = 0L; + protected Map> externalDetails; protected Command() { this.wait = 0; @@ -82,6 +102,10 @@ public String getContextParam(String name) { return contextMap.get(name); } + public Map getContextMap() { + return contextMap; + } + public boolean allowCaching() { return true; } @@ -94,6 +118,26 @@ public void setBypassHostMaintenance(boolean bypassHostMaintenance) { this.bypassHostMaintenance = bypassHostMaintenance; } + public boolean isReconcile() { + return false; + } + + public long getRequestSequence() { + return requestSequence; + } + + public void setRequestSequence(long requestSequence) { + this.requestSequence = requestSequence; + } + + public void setExternalDetails(Map> externalDetails) { + this.externalDetails = externalDetails; + } + + public Map> getExternalDetails() { + return externalDetails; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/api/src/main/java/com/cloud/agent/api/VgpuTypesInfo.java b/api/src/main/java/com/cloud/agent/api/VgpuTypesInfo.java index 85ffc1898209..5515a9c48bcb 100644 --- a/api/src/main/java/com/cloud/agent/api/VgpuTypesInfo.java +++ b/api/src/main/java/com/cloud/agent/api/VgpuTypesInfo.java @@ -15,10 +15,24 @@ // specific language governing permissions and limitations // under the License. package com.cloud.agent.api; + +import org.apache.cloudstack.gpu.GpuDevice; + public class VgpuTypesInfo { + private boolean passthroughEnabled = true; + private GpuDevice.DeviceType deviceType; + private String parentBusAddress; + private String busAddress; + private String numaNode; + private String pciRoot; + private String deviceId; + private String deviceName; + private String vendorId; + private String vendorName; private String modelName; private String groupName; + private String vmName; private Long maxHeads; private Long videoRam; private Long maxResolutionX; @@ -26,6 +40,7 @@ public class VgpuTypesInfo { private Long maxVgpuPerGpu; private Long remainingCapacity; private Long maxCapacity; + private boolean display = false; public String getModelName() { return modelName; @@ -39,22 +54,42 @@ public Long getVideoRam() { return videoRam; } + public void setVideoRam(Long videoRam) { + this.videoRam = videoRam; + } + public Long getMaxHeads() { return maxHeads; } + public void setMaxHeads(Long maxHeads) { + this.maxHeads = maxHeads; + } + public Long getMaxResolutionX() { return maxResolutionX; } + public void setMaxResolutionX(Long maxResolutionX) { + this.maxResolutionX = maxResolutionX; + } + public Long getMaxResolutionY() { return maxResolutionY; } + public void setMaxResolutionY(Long maxResolutionY) { + this.maxResolutionY = maxResolutionY; + } + public Long getMaxVpuPerGpu() { return maxVgpuPerGpu; } + public void setMaxVgpuPerGpu(Long maxVgpuPerGpu) { + this.maxVgpuPerGpu = maxVgpuPerGpu; + } + public Long getRemainingCapacity() { return remainingCapacity; } @@ -71,8 +106,133 @@ public void setMaxVmCapacity(Long maxCapacity) { this.maxCapacity = maxCapacity; } - public VgpuTypesInfo(String groupName, String modelName, Long videoRam, Long maxHeads, Long maxResolutionX, Long maxResolutionY, Long maxVgpuPerGpu, - Long remainingCapacity, Long maxCapacity) { + public boolean isPassthroughEnabled() { + return passthroughEnabled; + } + + public void setPassthroughEnabled(boolean passthroughEnabled) { + this.passthroughEnabled = passthroughEnabled; + } + + public GpuDevice.DeviceType getDeviceType() { + return deviceType; + } + + public void setDeviceType(GpuDevice.DeviceType deviceType) { + this.deviceType = deviceType; + } + + public String getParentBusAddress() { + return parentBusAddress; + } + + public void setParentBusAddress(String parentBusAddress) { + this.parentBusAddress = parentBusAddress; + } + + public String getBusAddress() { + return busAddress; + } + + public void setBusAddress(String busAddress) { + this.busAddress = busAddress; + } + + public String getNumaNode() { + return numaNode; + } + + public void setNumaNode(String numaNode) { + this.numaNode = numaNode; + } + + public String getPciRoot() { + return pciRoot; + } + + public void setPciRoot(String pciRoot) { + this.pciRoot = pciRoot; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getDeviceName() { + return deviceName; + } + + public void setDeviceName(String deviceName) { + this.deviceName = deviceName; + } + + public String getVendorId() { + return vendorId; + } + + public void setVendorId(String vendorId) { + this.vendorId = vendorId; + } + + public String getVendorName() { + return vendorName; + } + + public void setVendorName(String vendorName) { + this.vendorName = vendorName; + } + + public String getVmName() { + return vmName; + } + + public void setVmName(String vmName) { + this.vmName = vmName; + } + + public boolean isDisplay() { + return display; + } + + public void setDisplay(boolean display) { + this.display = display; + } + + public VgpuTypesInfo(GpuDevice.DeviceType deviceType, String groupName, String modelName, String busAddress, + String vendorId, String vendorName, String deviceId, String deviceName, String numaNode, String pciRoot + ) { + this.deviceType = deviceType; + this.groupName = groupName; + this.modelName = modelName; + this.busAddress = busAddress; + this.deviceId = deviceId; + this.deviceName = deviceName; + this.vendorId = vendorId; + this.vendorName = vendorName; + this.numaNode = numaNode; + this.pciRoot = pciRoot; + } + + public VgpuTypesInfo(GpuDevice.DeviceType deviceType, String groupName, String modelName, String busAddress, + String vendorId, String vendorName, String deviceId, String deviceName + ) { + this.deviceType = deviceType; + this.groupName = groupName; + this.modelName = modelName; + this.busAddress = busAddress; + this.deviceId = deviceId; + this.deviceName = deviceName; + this.vendorId = vendorId; + this.vendorName = vendorName; + } + + public VgpuTypesInfo(String groupName, String modelName, Long videoRam, Long maxHeads, Long maxResolutionX, + Long maxResolutionY, Long maxVgpuPerGpu, Long remainingCapacity, Long maxCapacity + ) { this.groupName = groupName; this.modelName = modelName; this.videoRam = videoRam; diff --git a/api/src/main/java/com/cloud/agent/api/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 new file mode 100644 index 000000000000..f7e4bfea80fb --- /dev/null +++ b/api/src/main/java/com/cloud/agent/api/to/BucketTO.java @@ -0,0 +1,50 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.agent.api.to; + +import org.apache.cloudstack.storage.object.Bucket; + +public final class BucketTO { + + private String name; + + private String accessKey; + + private String secretKey; + + public BucketTO(Bucket bucket) { + this.name = bucket.getName(); + this.accessKey = bucket.getAccessKey(); + this.secretKey = bucket.getSecretKey(); + } + + public BucketTO(String name) { + this.name = name; + } + + public String getName() { + return this.name; + } + + public String getAccessKey() { + return this.accessKey; + } + + public String getSecretKey() { + return this.secretKey; + } +} diff --git a/api/src/main/java/com/cloud/agent/api/to/DiskTO.java b/api/src/main/java/com/cloud/agent/api/to/DiskTO.java index d22df2df172e..5664de790919 100644 --- a/api/src/main/java/com/cloud/agent/api/to/DiskTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/DiskTO.java @@ -46,7 +46,7 @@ public class DiskTO { private Long diskSeq; private String path; private Volume.Type type; - private Map _details; + private Map details; public DiskTO() { @@ -92,10 +92,10 @@ public void setType(Volume.Type type) { } public void setDetails(Map details) { - _details = details; + this.details = details; } public Map getDetails() { - return _details; + return details; } } diff --git a/api/src/main/java/com/cloud/agent/api/to/FirewallRuleTO.java b/api/src/main/java/com/cloud/agent/api/to/FirewallRuleTO.java index d08884d1cbe0..69350815be3a 100644 --- a/api/src/main/java/com/cloud/agent/api/to/FirewallRuleTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/FirewallRuleTO.java @@ -47,7 +47,7 @@ public class FirewallRuleTO implements InternalIdentity { int[] srcPortRange; boolean revoked; boolean alreadyAdded; - private List sourceCidrList; + protected List sourceCidrList; private List destCidrList; FirewallRule.Purpose purpose; private Integer icmpType; @@ -155,9 +155,7 @@ public FirewallRuleTO(FirewallRule rule, String srcVlanTag, String srcIp, Firewa rule.getIcmpType(), rule.getIcmpCode()); this.trafficType = trafficType; - if (FirewallRule.Purpose.Ipv6Firewall.equals(purpose)) { - this.destCidrList = rule.getDestinationCidrList(); - } + this.destCidrList = rule.getDestinationCidrList(); } public FirewallRuleTO(FirewallRule rule, String srcVlanTag, String srcIp, FirewallRule.Purpose purpose, FirewallRule.TrafficType trafficType, diff --git a/api/src/main/java/com/cloud/agent/api/to/GPUDeviceTO.java b/api/src/main/java/com/cloud/agent/api/to/GPUDeviceTO.java index 4afe080477b7..6e9cee06dd38 100644 --- a/api/src/main/java/com/cloud/agent/api/to/GPUDeviceTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/GPUDeviceTO.java @@ -16,7 +16,9 @@ // under the License. package com.cloud.agent.api.to; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import com.cloud.agent.api.VgpuTypesInfo; @@ -24,9 +26,23 @@ public class GPUDeviceTO { private String gpuGroup; private String vgpuType; + private int gpuCount; private HashMap> groupDetails = new HashMap>(); + private List gpuDevices = new ArrayList<>(); - public GPUDeviceTO( String gpuGroup, String vgpuType, HashMap> groupDetails) { + public GPUDeviceTO(String gpuGroup, String vgpuType, int gpuCount, + HashMap> groupDetails, + List gpuDevices) { + this.gpuGroup = gpuGroup; + this.vgpuType = vgpuType; + this.groupDetails = groupDetails; + this.gpuCount = gpuCount; + this.gpuDevices = gpuDevices; + + } + + public GPUDeviceTO(String gpuGroup, String vgpuType, + HashMap> groupDetails) { this.gpuGroup = gpuGroup; this.vgpuType = vgpuType; this.groupDetails = groupDetails; @@ -48,6 +64,14 @@ public void setVgpuType(String vgpuType) { this.vgpuType = vgpuType; } + public int getGpuCount() { + return gpuCount; + } + + public void setGpuCount(int gpuCount) { + this.gpuCount = gpuCount; + } + public HashMap> getGroupDetails() { return groupDetails; } @@ -56,4 +80,11 @@ public void setGroupDetails(HashMap> grou this.groupDetails = groupDetails; } + public List getGpuDevices() { + return gpuDevices; + } + + public void setGpuDevices(List gpuDevices) { + this.gpuDevices = gpuDevices; + } } diff --git a/api/src/main/java/com/cloud/agent/api/to/LoadBalancerTO.java b/api/src/main/java/com/cloud/agent/api/to/LoadBalancerTO.java index 966d24886fef..6c4b9e607c51 100644 --- a/api/src/main/java/com/cloud/agent/api/to/LoadBalancerTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/LoadBalancerTO.java @@ -71,7 +71,7 @@ public LoadBalancerTO(String uuid, String srcIp, int srcPort, String protocol, S this.destinations = new DestinationTO[destinations.size()]; this.stickinessPolicies = null; this.sslCert = null; - this.lbProtocol = null; + this.lbProtocol = protocol; int i = 0; for (LbDestination destination : destinations) { this.destinations[i++] = new DestinationTO(destination.getIpAddress(), destination.getDestinationPortStart(), destination.isRevoked(), false); @@ -205,6 +205,10 @@ public LbSslCert getSslCert() { return this.sslCert; } + public void setLbSslCert(LbSslCert sslCert) { + this.sslCert = sslCert; + } + public String getSrcIpVlan() { return srcIpVlan; } @@ -374,13 +378,15 @@ public String getMonitorState() { public static class CounterTO implements Serializable { private static final long serialVersionUID = 2L; private final Long id; + private final String uuid; private final String name; private final Counter.Source source; private final String value; private final String provider; - public CounterTO(Long id, String name, Counter.Source source, String value, String provider) { + public CounterTO(Long id, String uuid, String name, Counter.Source source, String value, String provider) { this.id = id; + this.uuid = uuid; this.name = name; this.source = source; this.value = value; @@ -391,6 +397,10 @@ public Long getId() { return id; } + public String getUuid() { + return uuid; + } + public String getName() { return name; } @@ -411,12 +421,14 @@ public String getProvider() { public static class ConditionTO implements Serializable { private static final long serialVersionUID = 2L; private final Long id; + private final String uuid; private final long threshold; private final Condition.Operator relationalOperator; private final CounterTO counter; - public ConditionTO(Long id, long threshold, Condition.Operator relationalOperator, CounterTO counter) { + public ConditionTO(Long id, String uuid, long threshold, Condition.Operator relationalOperator, CounterTO counter) { this.id = id; + this.uuid = uuid; this.threshold = threshold; this.relationalOperator = relationalOperator; this.counter = counter; @@ -426,6 +438,10 @@ public Long getId() { return id; } + public String getUuid() { + return uuid; + } + public long getThreshold() { return threshold; } @@ -442,6 +458,7 @@ public CounterTO getCounter() { public static class AutoScalePolicyTO implements Serializable { private static final long serialVersionUID = 2L; private final long id; + private final String uuid; private final int duration; private final int quietTime; private final Date lastQuietTime; @@ -449,8 +466,9 @@ public static class AutoScalePolicyTO implements Serializable { boolean revoked; private final List conditions; - public AutoScalePolicyTO(long id, int duration, int quietTime, Date lastQuietTime, AutoScalePolicy.Action action, List conditions, boolean revoked) { + public AutoScalePolicyTO(long id, String uuid, int duration, int quietTime, Date lastQuietTime, AutoScalePolicy.Action action, List conditions, boolean revoked) { this.id = id; + this.uuid = uuid; this.duration = duration; this.quietTime = quietTime; this.lastQuietTime = lastQuietTime; @@ -463,6 +481,10 @@ public long getId() { return id; } + public String getUuid() { + return uuid; + } + public int getDuration() { return duration; } diff --git a/api/src/main/java/com/cloud/agent/api/to/NetworkTO.java b/api/src/main/java/com/cloud/agent/api/to/NetworkTO.java index bd08ce811013..d65ec0e3daad 100644 --- a/api/src/main/java/com/cloud/agent/api/to/NetworkTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/NetworkTO.java @@ -36,7 +36,7 @@ public class NetworkTO { protected TrafficType type; protected URI broadcastUri; protected URI isolationUri; - protected boolean isSecurityGroupEnabled; + protected boolean securityGroupEnabled; protected String name; protected String ip6address; protected String ip6gateway; @@ -112,7 +112,7 @@ public String getName() { } public void setSecurityGroupEnabled(boolean enabled) { - this.isSecurityGroupEnabled = enabled; + this.securityGroupEnabled = enabled; } /** @@ -221,7 +221,7 @@ public void setIsolationuri(URI isolationUri) { } public boolean isSecurityGroupEnabled() { - return this.isSecurityGroupEnabled; + return this.securityGroupEnabled; } public void setIp6Dns1(String ip6Dns1) { diff --git a/api/src/main/java/com/cloud/agent/api/to/NfsTO.java b/api/src/main/java/com/cloud/agent/api/to/NfsTO.java index 0f6511e83114..eeddbf649a77 100644 --- a/api/src/main/java/com/cloud/agent/api/to/NfsTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/NfsTO.java @@ -17,6 +17,7 @@ package com.cloud.agent.api.to; import com.cloud.storage.DataStoreRole; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; public class NfsTO implements DataStoreTO { @@ -41,6 +42,13 @@ public NfsTO(String url, DataStoreRole role) { } + @Override + public String toString() { + return String.format("NfsTO %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "uuid", "_url", "_role", "nfsVersion")); + } + @Override public String getUrl() { return _url; 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 3a616170d7e0..ca95fcfd6790 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 @@ -32,6 +32,9 @@ public class NicTO extends NetworkTO { Map details; boolean dpdkEnabled; Integer mtu; + Long networkId; + + String networkSegmentName; public NicTO() { super(); @@ -83,6 +86,14 @@ public void setUuid(String uuid) { this.nicUuid = uuid; } + public String getNicUuid() { + return nicUuid; + } + + public void setNicUuid(String nicUuid) { + this.nicUuid = nicUuid; + } + @Override public String toString() { return new StringBuilder("[Nic:").append(type).append("-").append(ip).append("-").append(broadcastUri).append("]").toString(); @@ -127,4 +138,20 @@ public Integer getMtu() { public void setMtu(Integer mtu) { this.mtu = mtu; } + + public Long getNetworkId() { + return networkId; + } + + public void setNetworkId(Long networkId) { + this.networkId = networkId; + } + + public String getNetworkSegmentName() { + return networkSegmentName; + } + + public void setNetworkSegmentName(String networkSegmentName) { + this.networkSegmentName = networkSegmentName; + } } diff --git a/api/src/main/java/com/cloud/agent/api/to/PortForwardingRuleTO.java b/api/src/main/java/com/cloud/agent/api/to/PortForwardingRuleTO.java index 76d6d952814c..91f337c5f55b 100644 --- a/api/src/main/java/com/cloud/agent/api/to/PortForwardingRuleTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/PortForwardingRuleTO.java @@ -19,6 +19,7 @@ import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.PortForwardingRule; import com.cloud.utils.net.NetUtils; +import org.apache.commons.lang3.StringUtils; /** * PortForwardingRuleTO specifies one port forwarding rule. @@ -37,6 +38,7 @@ public PortForwardingRuleTO(PortForwardingRule rule, String srcVlanTag, String s super(rule, srcVlanTag, srcIp); this.dstIp = rule.getDestinationIpAddress().addr(); this.dstPortRange = new int[] {rule.getDestinationPortStart(), rule.getDestinationPortEnd()}; + this.sourceCidrList = rule.getSourceCidrList(); } public PortForwardingRuleTO(long id, String srcIp, int srcPortStart, int srcPortEnd, String dstIp, int dstPortStart, int dstPortEnd, String protocol, @@ -58,4 +60,11 @@ public String getStringDstPortRange() { return NetUtils.portRangeToString(dstPortRange); } + public String getSourceCidrListAsString() { + if (sourceCidrList != null) { + return StringUtils.join(sourceCidrList, ","); + } + return null; + } + } 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 6e7aa8b21e28..18737c584b34 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 @@ -18,40 +18,41 @@ */ package com.cloud.agent.api.to; +import java.io.Serializable; + import com.cloud.agent.api.LogLevel; import com.cloud.hypervisor.Hypervisor; -import java.io.Serializable; - public class RemoteInstanceTO implements Serializable { private Hypervisor.HypervisorType hypervisorType; - private String hostName; private String instanceName; + private String instancePath; - // Vmware Remote Instances parameters + // VMware Remote Instances parameters (required for exporting OVA through ovftool) // TODO: cloud.agent.transport.Request#getCommands() cannot handle gsoc decode for polymorphic classes private String vcenterUsername; @LogLevel(LogLevel.Log4jLevel.Off) private String vcenterPassword; private String vcenterHost; private String datacenterName; - private String clusterName; public RemoteInstanceTO() { } - public RemoteInstanceTO(String hostName, String instanceName, String vcenterHost, - String datacenterName, String clusterName, - String vcenterUsername, String vcenterPassword) { + public RemoteInstanceTO(String instanceName) { + this.hypervisorType = Hypervisor.HypervisorType.VMware; + this.instanceName = instanceName; + } + + public RemoteInstanceTO(String instanceName, String instancePath, String vcenterHost, String vcenterUsername, String vcenterPassword, String datacenterName) { this.hypervisorType = Hypervisor.HypervisorType.VMware; - this.hostName = hostName; this.instanceName = instanceName; + this.instancePath = instancePath; this.vcenterHost = vcenterHost; - this.datacenterName = datacenterName; - this.clusterName = clusterName; this.vcenterUsername = vcenterUsername; this.vcenterPassword = vcenterPassword; + this.datacenterName = datacenterName; } public Hypervisor.HypervisorType getHypervisorType() { @@ -62,8 +63,8 @@ public String getInstanceName() { return this.instanceName; } - public String getHostName() { - return this.hostName; + public String getInstancePath() { + return this.instancePath; } public String getVcenterUsername() { @@ -81,8 +82,4 @@ public String getVcenterHost() { public String getDatacenterName() { return datacenterName; } - - public String getClusterName() { - return clusterName; - } } diff --git a/api/src/main/java/com/cloud/agent/api/to/S3TO.java b/api/src/main/java/com/cloud/agent/api/to/S3TO.java index 233238cf793d..936f8168b1e8 100644 --- a/api/src/main/java/com/cloud/agent/api/to/S3TO.java +++ b/api/src/main/java/com/cloud/agent/api/to/S3TO.java @@ -22,6 +22,7 @@ import com.cloud.agent.api.LogLevel.Log4jLevel; import com.cloud.storage.DataStoreRole; import com.cloud.utils.storage.S3.ClientOptions; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; public final class S3TO implements ClientOptions, DataStoreTO { @@ -68,6 +69,13 @@ public S3TO(final Long id, final String uuid, final String accessKey, final Stri } + @Override + public String toString() { + return String.format("S3TO %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "bucketName")); + } + public Long getId() { return this.id; } diff --git a/api/src/main/java/com/cloud/agent/api/to/StorageFilerTO.java b/api/src/main/java/com/cloud/agent/api/to/StorageFilerTO.java index e361e7a141fb..cbdb7922eb43 100644 --- a/api/src/main/java/com/cloud/agent/api/to/StorageFilerTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/StorageFilerTO.java @@ -19,6 +19,7 @@ import com.cloud.agent.api.LogLevel; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StoragePool; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; public class StorageFilerTO { long id; @@ -73,6 +74,6 @@ protected StorageFilerTO() { @Override public String toString() { - return new StringBuilder("Pool[").append(id).append("|").append(host).append(":").append(port).append("|").append(path).append("]").toString(); + return String.format("Pool %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "uuid", "host", "port", "path")); } } diff --git a/api/src/main/java/com/cloud/agent/api/to/SwiftTO.java b/api/src/main/java/com/cloud/agent/api/to/SwiftTO.java index b89dfea40e0c..14038566fbd3 100644 --- a/api/src/main/java/com/cloud/agent/api/to/SwiftTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/SwiftTO.java @@ -18,6 +18,7 @@ import com.cloud.storage.DataStoreRole; import com.cloud.utils.SwiftUtil; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; public class SwiftTO implements DataStoreTO, SwiftUtil.SwiftClientCfg { Long id; @@ -41,6 +42,13 @@ public SwiftTO(Long id, String url, String account, String userName, String key, this.storagePolicy = storagePolicy; } + @Override + public String toString() { + return String.format("SwiftTO %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "account", "userName")); + } + public Long getId() { return id; } 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 db6cba775483..e26cc1e9f029 100644 --- a/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java @@ -19,20 +19,22 @@ import java.util.List; import java.util.Map; import java.util.HashMap; +import java.util.stream.Collectors; import com.cloud.agent.api.LogLevel; import com.cloud.network.element.NetworkElement; import com.cloud.template.VirtualMachineTemplate.BootloaderType; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.Type; +import com.cloud.vm.VmDetailConstants; public class VirtualMachineTO { private long id; private String name; private BootloaderType bootloader; private VirtualMachine.State state; - Type type; - int cpus; + private Type type; + private int cpus; /** 'speed' is still here since 4.0.X/4.1.X management servers do not support @@ -43,46 +45,51 @@ public class VirtualMachineTO { So this is here for backwards compatibility with 4.0.X/4.1.X management servers and newer agents. */ - Integer speed; - Integer minSpeed; - Integer maxSpeed; - - long minRam; - long maxRam; - String hostName; - String arch; - String os; - String platformEmulator; - String bootArgs; - String[] bootupScripts; - boolean enableHA; - boolean limitCpuUse; - boolean enableDynamicallyScaleVm; + private Integer speed; + private Integer minSpeed; + private Integer maxSpeed; + + private long minRam; + private long maxRam; + private String hostName; + private String arch; + private String os; + private String platformEmulator; + private String bootArgs; + private String[] bootupScripts; + private boolean enableHA; + private boolean limitCpuUse; + private boolean enableDynamicallyScaleVm; @LogLevel(LogLevel.Log4jLevel.Off) - String vncPassword; - String vncAddr; - Map params; - String uuid; - String bootType; - String bootMode; - boolean enterHardwareSetup; - - DiskTO[] disks; - NicTO[] nics; - GPUDeviceTO gpuDevice; - Integer vcpuMaxLimit; - List vmData = null; - - String configDriveLabel = null; - String configDriveIsoRootFolder = null; - String configDriveIsoFile = null; - NetworkElement.Location configDriveLocation = NetworkElement.Location.SECONDARY; - - Double cpuQuotaPercentage = null; - - Map guestOsDetails = new HashMap(); - Map extraConfig = new HashMap<>(); - DeployAsIsInfoTO deployAsIsInfo; + private String vncPassword; + private String vncAddr; + private Map details; + private Map params; + private String uuid; + private String bootType; + private String bootMode; + private boolean enterHardwareSetup; + + private DiskTO[] disks; + private NicTO[] nics; + private GPUDeviceTO gpuDevice; + private Integer vcpuMaxLimit; + private List vmData = null; + + private String configDriveLabel = null; + private String configDriveIsoRootFolder = null; + private String configDriveIsoFile = null; + private NetworkElement.Location configDriveLocation = NetworkElement.Location.SECONDARY; + + private Double cpuQuotaPercentage = null; + + private Map guestOsDetails = new HashMap(); + private Map extraConfig = new HashMap<>(); + private Map networkIdToNetworkNameMap = new HashMap<>(); + 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) { @@ -188,7 +195,11 @@ public Integer getMaxSpeed() { return maxSpeed; } - public boolean getLimitCpuUse() { + public boolean isEnableHA() { + return enableHA; + } + + public boolean isLimitCpuUse() { return limitCpuUse; } @@ -253,6 +264,10 @@ public void setBootupScripts(String[] bootupScripts) { this.bootupScripts = bootupScripts; } + public void setEnableHA(boolean enableHA) { + this.enableHA = enableHA; + } + public DiskTO[] getDisks() { return disks; } @@ -286,11 +301,11 @@ public void setVncAddr(String vncAddr) { } public Map getDetails() { - return params; + return details; } public void setDetails(Map params) { - this.params = params; + this.details = params; } public String getUuid() { @@ -392,6 +407,14 @@ public Map getExtraConfig() { return extraConfig; } + public Map getNetworkIdToNetworkNameMap() { + return networkIdToNetworkNameMap; + } + + public void setNetworkIdToNetworkNameMap(Map networkIdToNetworkNameMap) { + this.networkIdToNetworkNameMap = networkIdToNetworkNameMap; + } + public String getBootType() { return bootType; } @@ -420,8 +443,80 @@ public void setDeployAsIsInfo(DeployAsIsInfoTO deployAsIsInfo) { this.deployAsIsInfo = deployAsIsInfo; } + public void setSpeed(Integer speed) { + this.speed = speed; + } + + public void setMinSpeed(Integer minSpeed) { + this.minSpeed = minSpeed; + } + + public void setMaxSpeed(Integer maxSpeed) { + this.maxSpeed = maxSpeed; + } + + public void setMinRam(long minRam) { + this.minRam = minRam; + } + + public void setMaxRam(long maxRam) { + this.maxRam = maxRam; + } + + public void setLimitCpuUse(boolean limitCpuUse) { + this.limitCpuUse = limitCpuUse; + } + + public Map getParams() { + return params; + } + + public void setParams(Map params) { + this.params = params; + } + + public void setExtraConfig(Map extraConfig) { + this.extraConfig = extraConfig; + } + + public String getMetadataManufacturer() { + return metadataManufacturer; + } + + public void setMetadataManufacturer(String metadataManufacturer) { + this.metadataManufacturer = metadataManufacturer; + } + + public String getMetadataProductName() { + return metadataProductName; + } + + 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); } + + public Map getExternalDetails() { + if (details == null) { + return new HashMap<>(); + } + return details.entrySet().stream() + .filter(entry -> entry.getKey().startsWith(VmDetailConstants.EXTERNAL_DETAIL_PREFIX)) + .collect(Collectors.toMap( + entry -> entry.getKey().substring(VmDetailConstants.EXTERNAL_DETAIL_PREFIX.length()), + Map.Entry::getValue + )); + } } diff --git a/api/src/main/java/com/cloud/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/bgp/ASNumber.java b/api/src/main/java/com/cloud/bgp/ASNumber.java new file mode 100644 index 000000000000..b0e5394df75e --- /dev/null +++ b/api/src/main/java/com/cloud/bgp/ASNumber.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.bgp; + +import org.apache.cloudstack.acl.InfrastructureEntity; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +public interface ASNumber extends InfrastructureEntity, InternalIdentity, Identity { + + Long getAccountId(); + Long getDomainId(); + long getAsNumber(); + long getAsNumberRangeId(); + long getDataCenterId(); + Date getAllocatedTime(); + boolean isAllocated(); + Long getNetworkId(); + Long getVpcId(); + Date getCreated(); + Date getRemoved(); +} diff --git a/api/src/main/java/com/cloud/bgp/ASNumberRange.java b/api/src/main/java/com/cloud/bgp/ASNumberRange.java new file mode 100644 index 000000000000..ae877ee60db7 --- /dev/null +++ b/api/src/main/java/com/cloud/bgp/ASNumberRange.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.bgp; + +import org.apache.cloudstack.acl.InfrastructureEntity; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +public interface ASNumberRange extends InfrastructureEntity, InternalIdentity, Identity { + + long getStartASNumber(); + long getEndASNumber(); + long getDataCenterId(); + Date getCreated(); +} diff --git a/api/src/main/java/com/cloud/bgp/BGPService.java b/api/src/main/java/com/cloud/bgp/BGPService.java new file mode 100644 index 000000000000..61d149f28470 --- /dev/null +++ b/api/src/main/java/com/cloud/bgp/BGPService.java @@ -0,0 +1,44 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.bgp; + +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.network.vpc.Vpc; +import com.cloud.utils.Pair; +import org.apache.cloudstack.api.command.user.bgp.ListASNumbersCmd; +import org.apache.cloudstack.network.BgpPeer; + +import java.util.List; + +public interface BGPService { + + ASNumberRange createASNumberRange(long zoneId, long startASNumber, long endASNumber); + List listASNumberRanges(Long zoneId); + Pair, Integer> listASNumbers(ListASNumbersCmd cmd); + boolean allocateASNumber(long zoneId, Long asNumber, Long networkId, Long vpcId); + Pair releaseASNumber(long zoneId, long asNumber, boolean isReleaseNetworkDestroy); + boolean deleteASRange(long id); + + boolean applyBgpPeers(Network network, boolean continueOnError) throws ResourceUnavailableException; + + boolean applyBgpPeers(Vpc vpc, boolean continueOnError) throws ResourceUnavailableException; + + List getBgpPeersForNetwork(Network network); + + List getBgpPeersForVpc(Vpc vpc); +} diff --git a/api/src/main/java/com/cloud/capacity/Capacity.java b/api/src/main/java/com/cloud/capacity/Capacity.java index 684490a605c3..4e584b18feee 100644 --- a/api/src/main/java/com/cloud/capacity/Capacity.java +++ b/api/src/main/java/com/cloud/capacity/Capacity.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.capacity; +import java.util.List; + import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; @@ -32,9 +34,18 @@ public interface Capacity extends InternalIdentity, Identity { public static final short CAPACITY_TYPE_LOCAL_STORAGE = 9; public static final short CAPACITY_TYPE_VIRTUAL_NETWORK_IPV6_SUBNET = 10; public static final short CAPACITY_TYPE_GPU = 19; + public static final short CAPACITY_TYPE_OBJECT_STORAGE = 20; + public static final short CAPACITY_TYPE_BACKUP_STORAGE = 21; public static final short CAPACITY_TYPE_CPU_CORE = 90; + public static final List STORAGE_CAPACITY_TYPES = List.of(CAPACITY_TYPE_STORAGE, + CAPACITY_TYPE_STORAGE_ALLOCATED, + CAPACITY_TYPE_SECONDARY_STORAGE, + CAPACITY_TYPE_LOCAL_STORAGE, + CAPACITY_TYPE_BACKUP_STORAGE, + CAPACITY_TYPE_OBJECT_STORAGE); + public Long getHostOrPoolId(); public Long getDataCenterId(); @@ -54,4 +65,6 @@ public interface Capacity extends InternalIdentity, Identity { public Float getUsedPercentage(); public Long getAllocatedCapacity(); + + public String getTag(); } diff --git a/api/src/main/java/com/cloud/configuration/ConfigurationService.java b/api/src/main/java/com/cloud/configuration/ConfigurationService.java index 97d4b42974b3..438283136d2c 100644 --- a/api/src/main/java/com/cloud/configuration/ConfigurationService.java +++ b/api/src/main/java/com/cloud/configuration/ConfigurationService.java @@ -17,7 +17,11 @@ package com.cloud.configuration; import java.util.List; +import java.util.Map; +import java.util.Objects; +import com.cloud.network.Network; +import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.command.admin.config.ResetCfgCmd; import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd; import org.apache.cloudstack.api.command.admin.network.CreateGuestNetworkIpv6PrefixCmd; @@ -104,36 +108,22 @@ public interface ConfigurationService { /** * 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); @@ -143,7 +133,6 @@ public interface ConfigurationService { * @param cmd * - the command specifying diskOfferingId, name, description, tags * @return updated disk offering - * @throws */ DiskOffering updateDiskOffering(UpdateDiskOfferingCmd cmd); @@ -153,34 +142,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); @@ -201,11 +178,10 @@ public interface ConfigurationService { * TODO * @param allocationState * TODO + * @param storageAccessGroups * @return the new pod if successful, null otherwise - * @throws - * @throws */ - Pod createPod(long zoneId, String name, String startIp, String endIp, String gateway, String netmask, String allocationState); + Pod createPod(long zoneId, String name, String startIp, String endIp, String gateway, String netmask, String allocationState, List storageAccessGroups); /** * Creates a mutual exclusive IP range in the pod with same gateway, netmask. @@ -223,8 +199,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; @@ -245,9 +220,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); @@ -257,17 +229,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); @@ -290,22 +257,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, @@ -320,9 +272,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); @@ -344,14 +293,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); @@ -372,4 +319,16 @@ Vlan updateVlanAndPublicIpRange(UpdateVlanIpRangeCmd cmd) throws ConcurrentOpera List listPortableIps(long id); Boolean isAccountAllowedToCreateOfferingsWithTags(IsAccountAllowedToCreateOfferingsWithTagsCmd cmd); + + public static final Map ProviderDetailKeyMap = Map.of( + Network.Provider.Nsx.getName(), ApiConstants.NSX_DETAIL_KEY, + Network.Provider.Netris.getName(), ApiConstants.NETRIS_DETAIL_KEY + ); + + public static boolean IsIpRangeForProvider(Network.Provider provider) { + if (Objects.isNull(provider)) { + return false; + } + return ProviderDetailKeyMap.containsKey(provider.getName()); + } } diff --git a/api/src/main/java/com/cloud/configuration/Resource.java b/api/src/main/java/com/cloud/configuration/Resource.java index 32db2fcafeaf..97be7f9d64c5 100644 --- a/api/src/main/java/com/cloud/configuration/Resource.java +++ b/api/src/main/java/com/cloud/configuration/Resource.java @@ -21,7 +21,7 @@ public interface Resource { short RESOURCE_UNLIMITED = -1; String UNLIMITED = "Unlimited"; - enum ResourceType { // Primary and Secondary storage are allocated_storage and not the physical storage. + enum ResourceType { // All storage type resources are allocated_storage and not the physical storage. user_vm("user_vm", 0), public_ip("public_ip", 1), volume("volume", 2), @@ -33,7 +33,12 @@ enum ResourceType { // Primary and Secondary storage are allocated_storage and n cpu("cpu", 8), memory("memory", 9), primary_storage("primary_storage", 10), - secondary_storage("secondary_storage", 11); + secondary_storage("secondary_storage", 11), + backup("backup", 12), + backup_storage("backup_storage", 13), + bucket("bucket", 14), + object_storage("object_storage", 15), + gpu("gpu", 16); private String name; private int ordinal; @@ -62,6 +67,10 @@ public static ResourceType fromOrdinal(int ordinal) { } return null; } + + public static Boolean isStorageType(ResourceType type) { + return (type == primary_storage || type == secondary_storage || type == backup_storage || type == object_storage); + } } public static class ResourceOwnerType { @@ -85,5 +94,6 @@ public String getName() { long getOwnerId(); ResourceOwnerType getResourceOwnerType(); + String getTag(); } diff --git a/api/src/main/java/com/cloud/cpu/CPU.java b/api/src/main/java/com/cloud/cpu/CPU.java new file mode 100644 index 000000000000..11b38b73da53 --- /dev/null +++ b/api/src/main/java/com/cloud/cpu/CPU.java @@ -0,0 +1,71 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.cpu; + +import org.apache.commons.lang3.StringUtils; + +public class CPU { + public enum CPUArch { + x86("i686", 32), + amd64("x86_64", 64), + arm64("aarch64", 64), + s390x("s390x", 64); + + private final String type; + private final int bits; + + CPUArch(String type, int bits) { + this.type = type; + this.bits = bits; + } + + public static CPUArch getDefault() { + return amd64; + } + + public String getType() { + return type; + } + + public int getBits() { + return bits; + } + + public static CPUArch fromType(String type) { + if (StringUtils.isBlank(type)) { + return getDefault(); + } + for (CPUArch arch : values()) { + if (arch.type.equals(type)) { + return arch; + } + } + throw new IllegalArgumentException("Unsupported arch type: " + type); + } + + public static String getTypesAsCSV() { + StringBuilder sb = new StringBuilder(); + for (CPUArch arch : values()) { + sb.append(arch.getType()).append(","); + } + if (sb.length() > 0) { + sb.setLength(sb.length() - 1); + } + return sb.toString(); + } + } +} diff --git a/api/src/main/java/com/cloud/dc/DedicatedResources.java b/api/src/main/java/com/cloud/dc/DedicatedResources.java index 63188ca0b0e9..23e6cc88a1e0 100644 --- a/api/src/main/java/com/cloud/dc/DedicatedResources.java +++ b/api/src/main/java/com/cloud/dc/DedicatedResources.java @@ -21,6 +21,10 @@ import org.apache.cloudstack.api.InternalIdentity; public interface DedicatedResources extends InfrastructureEntity, InternalIdentity, Identity { + enum Type { + Zone, Pod, Cluster, Host + } + @Override long getId(); diff --git a/api/src/main/java/com/cloud/dc/Pod.java b/api/src/main/java/com/cloud/dc/Pod.java index 1cbab36f3bd4..17c5b615d4b6 100644 --- a/api/src/main/java/com/cloud/dc/Pod.java +++ b/api/src/main/java/com/cloud/dc/Pod.java @@ -43,4 +43,6 @@ public interface Pod extends InfrastructureEntity, Grouping, Identity, InternalI AllocationState getAllocationState(); boolean getExternalDhcp(); + + String getStorageAccessGroups(); } diff --git a/api/src/main/java/com/cloud/deploy/DeploymentClusterPlanner.java b/api/src/main/java/com/cloud/deploy/DeploymentClusterPlanner.java index a668b79187dc..9471c3d5c84c 100644 --- a/api/src/main/java/com/cloud/deploy/DeploymentClusterPlanner.java +++ b/api/src/main/java/com/cloud/deploy/DeploymentClusterPlanner.java @@ -57,6 +57,17 @@ public interface DeploymentClusterPlanner extends DeploymentPlanner { false, ConfigKey.Scope.Global); + static final ConfigKey VmAllocationAlgorithm = new ConfigKey<>( + String.class, + "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', or 'firstfitleastconsumed'.", + true, + ConfigKey.Scope.Global, null, null, null, null, null, + ConfigKey.Kind.Select, + "random,firstfit,userdispersing,firstfitleastconsumed"); + /** * This is called to determine list of possible clusters where a virtual * machine can be deployed. diff --git a/api/src/main/java/com/cloud/deploy/DeploymentPlanner.java b/api/src/main/java/com/cloud/deploy/DeploymentPlanner.java index e9f706ac1cee..8f7e773070f0 100644 --- a/api/src/main/java/com/cloud/deploy/DeploymentPlanner.java +++ b/api/src/main/java/com/cloud/deploy/DeploymentPlanner.java @@ -21,8 +21,12 @@ import java.util.HashSet; import java.util.Set; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; + import com.cloud.dc.DataCenter; import com.cloud.dc.Pod; +import com.cloud.exception.CloudException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InsufficientServerCapacityException; import com.cloud.exception.ResourceUnavailableException; @@ -66,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; } public enum PlannerResourceUsage { @@ -75,7 +79,7 @@ public enum PlannerResourceUsage { public static class ExcludeList implements Serializable { private static final long serialVersionUID = -482175549460148301L; - + protected static Logger LOGGER = LogManager.getLogger(ExcludeList.class); private Set _dcIds; private Set _podIds; private Set _clusterIds; @@ -104,13 +108,26 @@ public ExcludeList(Set dcIds, Set podIds, Set clusterIds, Set< } } + private void logAvoid(Class scope, CloudException e) { + Long id = null; + if (e instanceof InsufficientCapacityException) { + id = ((InsufficientCapacityException) e).getId(); + } else if (e instanceof ResourceUnavailableException) { + id = ((ResourceUnavailableException) e).getResourceId(); + } else { + LOGGER.debug("Failed to log avoided component due to unexpected exception type [{}].", e.getMessage()); + return; + } + LOGGER.debug("Adding {} [{}] to the avoid set due to [{}].", scope.getSimpleName(), id, e.getMessage()); + } + public boolean add(InsufficientCapacityException e) { Class scope = e.getScope(); if (scope == null) { return false; } - + logAvoid(scope, e); if (Host.class.isAssignableFrom(scope)) { addHost(e.getId()); } else if (Pod.class.isAssignableFrom(scope)) { @@ -128,13 +145,14 @@ public boolean add(InsufficientCapacityException e) { return true; } + public boolean add(ResourceUnavailableException e) { Class scope = e.getScope(); if (scope == null) { return false; } - + logAvoid(scope, e); if (Host.class.isAssignableFrom(scope)) { addHost(e.getResourceId()); } else if (Pod.class.isAssignableFrom(scope)) { diff --git a/api/src/main/java/com/cloud/event/EventTypes.java b/api/src/main/java/com/cloud/event/EventTypes.java index 5d5252290959..6524d9de15e0 100644 --- a/api/src/main/java/com/cloud/event/EventTypes.java +++ b/api/src/main/java/com/cloud/event/EventTypes.java @@ -27,10 +27,21 @@ 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.extension.Extension; +import org.apache.cloudstack.extension.ExtensionCustomAction; +import org.apache.cloudstack.gpu.GpuCard; +import org.apache.cloudstack.gpu.GpuDevice; +import org.apache.cloudstack.gpu.VgpuProfile; import org.apache.cloudstack.ha.HAConfig; +import org.apache.cloudstack.network.BgpPeer; +import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap; +import org.apache.cloudstack.quota.QuotaTariff; import org.apache.cloudstack.storage.object.Bucket; import org.apache.cloudstack.storage.object.ObjectStore; +import org.apache.cloudstack.storage.sharedfs.SharedFS; import org.apache.cloudstack.usage.Usage; import org.apache.cloudstack.vm.schedule.VMSchedule; @@ -241,6 +252,8 @@ public class EventTypes { public static final String EVENT_ROLE_UPDATE = "ROLE.UPDATE"; public static final String EVENT_ROLE_DELETE = "ROLE.DELETE"; public static final String EVENT_ROLE_IMPORT = "ROLE.IMPORT"; + public static final String EVENT_ROLE_ENABLE = "ROLE.ENABLE"; + public static final String EVENT_ROLE_DISABLE = "ROLE.DISABLE"; public static final String EVENT_ROLE_PERMISSION_CREATE = "ROLE.PERMISSION.CREATE"; public static final String EVENT_ROLE_PERMISSION_UPDATE = "ROLE.PERMISSION.UPDATE"; public static final String EVENT_ROLE_PERMISSION_DELETE = "ROLE.PERMISSION.DELETE"; @@ -282,9 +295,13 @@ public class EventTypes { //registering userdata events public static final String EVENT_REGISTER_USER_DATA = "REGISTER.USER.DATA"; + public static final String EVENT_REGISTER_CNI_CONFIG = "REGISTER.CNI.CONFIG"; + public static final String EVENT_DELETE_CNI_CONFIG = "DELETE.CNI.CONFIG"; - //register for user API and secret keys + //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 public static final String EVENT_TEMPLATE_CREATE = "TEMPLATE.CREATE"; @@ -303,6 +320,7 @@ public class EventTypes { public static final String EVENT_VOLUME_CREATE = "VOLUME.CREATE"; public static final String EVENT_VOLUME_DELETE = "VOLUME.DELETE"; public static final String EVENT_VOLUME_ATTACH = "VOLUME.ATTACH"; + public static final String EVENT_VOLUME_CHECK = "VOLUME.CHECK"; public static final String EVENT_VOLUME_DETACH = "VOLUME.DETACH"; public static final String EVENT_VOLUME_EXTRACT = "VOLUME.EXTRACT"; public static final String EVENT_VOLUME_UPLOAD = "VOLUME.UPLOAD"; @@ -314,6 +332,8 @@ public class EventTypes { public static final String EVENT_VOLUME_UPDATE = "VOLUME.UPDATE"; public static final String EVENT_VOLUME_DESTROY = "VOLUME.DESTROY"; public static final String EVENT_VOLUME_RECOVER = "VOLUME.RECOVER"; + public static final String EVENT_VOLUME_IMPORT = "VOLUME.IMPORT"; + public static final String EVENT_VOLUME_UNMANAGE = "VOLUME.UNMANAGE"; public static final String EVENT_VOLUME_CHANGE_DISK_OFFERING = "VOLUME.CHANGE.DISK.OFFERING"; // Domains @@ -329,6 +349,7 @@ public class EventTypes { public static final String EVENT_SNAPSHOT_OFF_PRIMARY = "SNAPSHOT.OFF_PRIMARY"; public static final String EVENT_SNAPSHOT_DELETE = "SNAPSHOT.DELETE"; public static final String EVENT_SNAPSHOT_REVERT = "SNAPSHOT.REVERT"; + public static final String EVENT_SNAPSHOT_EXTRACT = "SNAPSHOT.EXTRACT"; public static final String EVENT_SNAPSHOT_POLICY_CREATE = "SNAPSHOTPOLICY.CREATE"; public static final String EVENT_SNAPSHOT_POLICY_UPDATE = "SNAPSHOTPOLICY.UPDATE"; public static final String EVENT_SNAPSHOT_POLICY_DELETE = "SNAPSHOTPOLICY.DELETE"; @@ -362,6 +383,21 @@ public class EventTypes { public static final String EVENT_DISK_OFFERING_EDIT = "DISK.OFFERING.EDIT"; public static final String EVENT_DISK_OFFERING_DELETE = "DISK.OFFERING.DELETE"; + // GPU Cards + public static final String EVENT_GPU_CARD_CREATE = "GPU.CARD.CREATE"; + public static final String EVENT_GPU_CARD_EDIT = "GPU.CARD.EDIT"; + public static final String EVENT_GPU_CARD_DELETE = "GPU.CARD.DELETE"; + + // vGPU Profile + public static final String EVENT_VGPU_PROFILE_CREATE = "VGPU.PROFILE.CREATE"; + public static final String EVENT_VGPU_PROFILE_EDIT = "VGPU.PROFILE.EDIT"; + public static final String EVENT_VGPU_PROFILE_DELETE = "VGPU.PROFILE.DELETE"; + + // GPU Devices + public static final String EVENT_GPU_DEVICE_CREATE = "GPU.DEVICE.CREATE"; + public static final String EVENT_GPU_DEVICE_EDIT = "GPU.DEVICE.EDIT"; + public static final String EVENT_GPU_DEVICE_DELETE = "GPU.DEVICE.DELETE"; + // Network offerings public static final String EVENT_NETWORK_OFFERING_CREATE = "NETWORK.OFFERING.CREATE"; public static final String EVENT_NETWORK_OFFERING_ASSIGN = "NETWORK.OFFERING.ASSIGN"; @@ -386,6 +422,11 @@ public class EventTypes { public static final String EVENT_VLAN_IP_RANGE_RELEASE = "VLAN.IP.RANGE.RELEASE"; public static final String EVENT_VLAN_IP_RANGE_UPDATE = "VLAN.IP.RANGE.UPDATE"; + // AS Number + public static final String EVENT_AS_RANGE_CREATE = "AS.RANGE.CREATE"; + public static final String EVENT_AS_RANGE_DELETE = "AS.RANGE.DELETE"; + public static final String EVENT_AS_NUMBER_RELEASE = "AS.NUMBER.RELEASE"; + public static final String EVENT_MANAGEMENT_IP_RANGE_CREATE = "MANAGEMENT.IP.RANGE.CREATE"; public static final String EVENT_MANAGEMENT_IP_RANGE_DELETE = "MANAGEMENT.IP.RANGE.DELETE"; public static final String EVENT_MANAGEMENT_IP_RANGE_UPDATE = "MANAGEMENT.IP.RANGE.UPDATE"; @@ -444,9 +485,12 @@ public class EventTypes { public static final String EVENT_MAINTENANCE_PREPARE_PRIMARY_STORAGE = "MAINT.PREPARE.PS"; // Primary storage pool + public static final String EVENT_UPDATE_PRIMARY_STORAGE = "UPDATE.PS"; public static final String EVENT_ENABLE_PRIMARY_STORAGE = "ENABLE.PS"; public static final String EVENT_DISABLE_PRIMARY_STORAGE = "DISABLE.PS"; public static final String EVENT_SYNC_STORAGE_POOL = "SYNC.STORAGE.POOL"; + public static final String EVENT_CONFIGURE_STORAGE_ACCESS = "CONFIGURE.STORAGE.ACCESS"; + public static final String EVENT_CHANGE_STORAGE_POOL_SCOPE = "CHANGE.STORAGE.POOL.SCOPE"; // VPN public static final String EVENT_REMOTE_ACCESS_VPN_CREATE = "VPN.REMOTE.ACCESS.CREATE"; @@ -460,6 +504,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"; @@ -477,6 +522,8 @@ public class EventTypes { public static final String EVENT_ZONE_VLAN_ASSIGN = "ZONE.VLAN.ASSIGN"; public static final String EVENT_ZONE_VLAN_RELEASE = "ZONE.VLAN.RELEASE"; + public static final String EVENT_ZONE_VXLAN_ASSIGN = "ZONE.VXLAN.ASSIGN"; + public static final String EVENT_ZONE_VXLAN_RELEASE = "ZONE.VXLAN.RELEASE"; // Projects public static final String EVENT_PROJECT_CREATE = "PROJECT.CREATE"; @@ -537,6 +584,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"; @@ -588,11 +636,13 @@ public class EventTypes { public static final String EVENT_VM_BACKUP_CREATE = "BACKUP.CREATE"; public static final String EVENT_VM_BACKUP_RESTORE = "BACKUP.RESTORE"; public static final String EVENT_VM_BACKUP_DELETE = "BACKUP.DELETE"; + public static final String EVENT_VM_BACKUP_OFFERING_REMOVED_AND_BACKUPS_DELETED = "BACKUP.OFFERING.BACKUPS.DEL"; public static final String EVENT_VM_BACKUP_RESTORE_VOLUME_TO_VM = "BACKUP.RESTORE.VOLUME.TO.VM"; public static final String EVENT_VM_BACKUP_SCHEDULE_CONFIGURE = "BACKUP.SCHEDULE.CONFIGURE"; public static final String EVENT_VM_BACKUP_SCHEDULE_DELETE = "BACKUP.SCHEDULE.DELETE"; public static final String EVENT_VM_BACKUP_USAGE_METRIC = "BACKUP.USAGE.METRIC"; public static final String EVENT_VM_BACKUP_EDIT = "BACKUP.OFFERING.EDIT"; + public static final String EVENT_VM_CREATE_FROM_BACKUP = "VM.CREATE.FROM.BACKUP"; // external network device events public static final String EVENT_EXTERNAL_NVP_CONTROLLER_ADD = "PHYSICAL.NVPCONTROLLER.ADD"; @@ -668,6 +718,9 @@ public class EventTypes { public static final String EVENT_EXTERNAL_OPENDAYLIGHT_CONFIGURE_CONTROLLER = "PHYSICAL.ODLCONTROLLER.CONFIGURE"; //Guest OS related events + public static final String EVENT_GUEST_OS_CATEGORY_ADD = "GUEST.OS.CATEGORY.ADD"; + public static final String EVENT_GUEST_OS_CATEGORY_DELETE = "GUEST.OS.CATEGORY.DELETE"; + public static final String EVENT_GUEST_OS_CATEGORY_UPDATE = "GUEST.OS.CATEGORY.UPDATE"; public static final String EVENT_GUEST_OS_ADD = "GUEST.OS.ADD"; public static final String EVENT_GUEST_OS_REMOVE = "GUEST.OS.REMOVE"; public static final String EVENT_GUEST_OS_UPDATE = "GUEST.OS.UPDATE"; @@ -717,6 +770,15 @@ public class EventTypes { // SystemVM public static final String EVENT_LIVE_PATCH_SYSTEMVM = "LIVE.PATCH.SYSTEM.VM"; + //Purge resources + public static final String EVENT_PURGE_EXPUNGED_RESOURCES = "PURGE.EXPUNGED.RESOURCES"; + + // Management Server + public static final String EVENT_MS_MAINTENANCE_PREPARE = "MS.MAINTENANCE.PREPARE"; + public static final String EVENT_MS_MAINTENANCE_CANCEL = "MS.MAINTENANCE.CANCEL"; + public static final String EVENT_MS_SHUTDOWN_PREPARE = "MS.SHUTDOWN.PREPARE"; + public static final String EVENT_MS_SHUTDOWN_CANCEL = "MS.SHUTDOWN.CANCEL"; + public static final String EVENT_MS_SHUTDOWN = "MS.SHUTDOWN"; // OBJECT STORE public static final String EVENT_OBJECT_STORE_CREATE = "OBJECT.STORE.CREATE"; @@ -728,6 +790,76 @@ public class EventTypes { public static final String EVENT_BUCKET_DELETE = "BUCKET.DELETE"; public static final String EVENT_BUCKET_UPDATE = "BUCKET.UPDATE"; + // Quota + public static final String EVENT_QUOTA_TARIFF_CREATE = "QUOTA.TARIFF.CREATE"; + public static final String EVENT_QUOTA_TARIFF_DELETE = "QUOTA.TARIFF.DELETE"; + public static final String EVENT_QUOTA_TARIFF_UPDATE = "QUOTA.TARIFF.UPDATE"; + + // Routing + public static final String EVENT_ZONE_IP4_SUBNET_CREATE = "ZONE.IP4.SUBNET.CREATE"; + public static final String EVENT_ZONE_IP4_SUBNET_UPDATE = "ZONE.IP4.SUBNET.UPDATE"; + public static final String EVENT_ZONE_IP4_SUBNET_DELETE = "ZONE.IP4.SUBNET.DELETE"; + public static final String EVENT_ZONE_IP4_SUBNET_DEDICATE = "ZONE.IP4.SUBNET.DEDICATE"; + public static final String EVENT_ZONE_IP4_SUBNET_RELEASE = "ZONE.IP4.SUBNET.RELEASE"; + public static final String EVENT_IP4_GUEST_SUBNET_CREATE = "IP4.GUEST.SUBNET.CREATE"; + public static final String EVENT_IP4_GUEST_SUBNET_DELETE = "IP4.GUEST.SUBNET.DELETE"; + public static final String EVENT_ROUTING_IPV4_FIREWALL_RULE_CREATE = "ROUTING.IPV4.FIREWALL.RULE.CREATE"; + public static final String EVENT_ROUTING_IPV4_FIREWALL_RULE_UPDATE = "ROUTING.IPV4.FIREWALL.RULE.UPDATE"; + public static final String EVENT_ROUTING_IPV4_FIREWALL_RULE_DELETE = "ROUTING.IPV4.FIREWALL.RULE.DELETE"; + public static final String EVENT_BGP_PEER_CREATE = "BGP.PEER.CREATE"; + public static final String EVENT_BGP_PEER_UPDATE = "BGP.PEER.UPDATE"; + public static final String EVENT_BGP_PEER_DELETE = "BGP.PEER.DELETE"; + public static final String EVENT_BGP_PEER_DEDICATE = "BGP.PEER.DEDICATE"; + public static final String EVENT_BGP_PEER_RELEASE = "BGP.PEER.RELEASE"; + public static final String EVENT_NETWORK_BGP_PEER_UPDATE = "NETWORK.BGP.PEER.UPDATE"; + public static final String EVENT_VPC_BGP_PEER_UPDATE = "VPC.BGP.PEER.UPDATE"; + + // SharedFS + public static final String EVENT_SHAREDFS_CREATE = "SHAREDFS.CREATE"; + public static final String EVENT_SHAREDFS_START = "SHAREDFS.START"; + public static final String EVENT_SHAREDFS_UPDATE = "SHAREDFS.UPDATE"; + public static final String EVENT_SHAREDFS_CHANGE_SERVICE_OFFERING = "SHAREDFS.CHANGE.SERVICE.OFFERING"; + public static final String EVENT_SHAREDFS_CHANGE_DISK_OFFERING = "SHAREDFS.CHANGE.DISK.OFFERING"; + public static final String EVENT_SHAREDFS_STOP = "SHAREDFS.STOP"; + public static final String EVENT_SHAREDFS_RESTART = "SHAREDFS.RESTART"; + public static final String EVENT_SHAREDFS_DESTROY = "SHAREDFS.DESTROY"; + public static final String EVENT_SHAREDFS_EXPUNGE = "SHAREDFS.EXPUNGE"; + public static final String EVENT_SHAREDFS_RECOVER = "SHAREDFS.RECOVER"; + + // Resource Limit + public static final String EVENT_RESOURCE_LIMIT_UPDATE = "RESOURCE.LIMIT.UPDATE"; + + // Management Server + public static final String EVENT_MANAGEMENT_SERVER_REMOVE = "MANAGEMENT.SERVER.REMOVE"; + + // VM Lease + public static final String VM_LEASE_EXPIRED = "VM.LEASE.EXPIRED"; + public static final String VM_LEASE_DISABLED = "VM.LEASE.DISABLED"; + public static final String VM_LEASE_CANCELLED = "VM.LEASE.CANCELLED"; + public static final String VM_LEASE_EXPIRING = "VM.LEASE.EXPIRING"; + + // GUI Theme + public static final String EVENT_GUI_THEME_CREATE = "GUI.THEME.CREATE"; + public static final String EVENT_GUI_THEME_REMOVE = "GUI.THEME.REMOVE"; + public static final String EVENT_GUI_THEME_UPDATE = "GUI.THEME.UPDATE"; + + // Extension + public static final String EVENT_EXTENSION_CREATE = "EXTENSION.CREATE"; + public static final String EVENT_EXTENSION_UPDATE = "EXTENSION.UPDATE"; + public static final String EVENT_EXTENSION_DELETE = "EXTENSION.DELETE"; + public static final String EVENT_EXTENSION_RESOURCE_REGISTER = "EXTENSION.RESOURCE.REGISTER"; + public static final String EVENT_EXTENSION_RESOURCE_UNREGISTER = "EXTENSION.RESOURCE.UNREGISTER"; + public static final String EVENT_EXTENSION_CUSTOM_ACTION_ADD = "EXTENSION.CUSTOM.ACTION.ADD"; + public static final String EVENT_EXTENSION_CUSTOM_ACTION_UPDATE = "EXTENSION.CUSTOM.ACTION.UPDATE"; + public static final String EVENT_EXTENSION_CUSTOM_ACTION_DELETE = "EXTENSION.CUSTOM.ACTION.DELETE"; + + // Custom Action + public static final String EVENT_CUSTOM_ACTION = "CUSTOM.ACTION"; + + // Backup Repository + public static final String EVENT_BACKUP_REPOSITORY_ADD = "BACKUP.REPOSITORY.ADD"; + public static final String EVENT_BACKUP_REPOSITORY_UPDATE = "BACKUP.REPOSITORY.UPDATE"; + static { // TODO: need a way to force author adding event types to declare the entity details as well, with out braking @@ -829,6 +961,8 @@ public class EventTypes { entityEventDetails.put(EVENT_ROLE_UPDATE, Role.class); entityEventDetails.put(EVENT_ROLE_DELETE, Role.class); entityEventDetails.put(EVENT_ROLE_IMPORT, Role.class); + entityEventDetails.put(EVENT_ROLE_ENABLE, Role.class); + entityEventDetails.put(EVENT_ROLE_DISABLE, Role.class); entityEventDetails.put(EVENT_ROLE_PERMISSION_CREATE, RolePermission.class); entityEventDetails.put(EVENT_ROLE_PERMISSION_UPDATE, RolePermission.class); entityEventDetails.put(EVENT_ROLE_PERMISSION_DELETE, RolePermission.class); @@ -885,6 +1019,7 @@ public class EventTypes { // Snapshots entityEventDetails.put(EVENT_SNAPSHOT_CREATE, Snapshot.class); entityEventDetails.put(EVENT_SNAPSHOT_DELETE, Snapshot.class); + entityEventDetails.put(EVENT_SNAPSHOT_EXTRACT, Snapshot.class); entityEventDetails.put(EVENT_SNAPSHOT_ON_PRIMARY, Snapshot.class); entityEventDetails.put(EVENT_SNAPSHOT_OFF_PRIMARY, Snapshot.class); entityEventDetails.put(EVENT_SNAPSHOT_POLICY_CREATE, SnapshotPolicy.class); @@ -919,6 +1054,21 @@ public class EventTypes { entityEventDetails.put(EVENT_DISK_OFFERING_EDIT, DiskOffering.class); entityEventDetails.put(EVENT_DISK_OFFERING_DELETE, DiskOffering.class); + // GPU Cards + entityEventDetails.put(EVENT_GPU_CARD_CREATE, GpuCard.class); + entityEventDetails.put(EVENT_GPU_CARD_EDIT, GpuCard.class); + entityEventDetails.put(EVENT_GPU_CARD_DELETE, GpuCard.class); + + // vGPU Profiles + entityEventDetails.put(EVENT_VGPU_PROFILE_CREATE, VgpuProfile.class); + entityEventDetails.put(EVENT_VGPU_PROFILE_EDIT, VgpuProfile.class); + entityEventDetails.put(EVENT_VGPU_PROFILE_DELETE, VgpuProfile.class); + + // GPU Devices + entityEventDetails.put(EVENT_GPU_DEVICE_CREATE, GpuDevice.class); + entityEventDetails.put(EVENT_GPU_DEVICE_EDIT, GpuDevice.class); + entityEventDetails.put(EVENT_GPU_DEVICE_DELETE, GpuDevice.class); + // Network offerings entityEventDetails.put(EVENT_NETWORK_OFFERING_CREATE, NetworkOffering.class); entityEventDetails.put(EVENT_NETWORK_OFFERING_ASSIGN, NetworkOffering.class); @@ -989,8 +1139,10 @@ public class EventTypes { entityEventDetails.put(EVENT_MAINTENANCE_PREPARE_PRIMARY_STORAGE, Host.class); // Primary storage pool + entityEventDetails.put(EVENT_UPDATE_PRIMARY_STORAGE, StoragePool.class); entityEventDetails.put(EVENT_ENABLE_PRIMARY_STORAGE, StoragePool.class); entityEventDetails.put(EVENT_DISABLE_PRIMARY_STORAGE, StoragePool.class); + entityEventDetails.put(EVENT_CHANGE_STORAGE_POOL_SCOPE, StoragePool.class); // VPN entityEventDetails.put(EVENT_REMOTE_ACCESS_VPN_CREATE, RemoteAccessVpn.class); @@ -1002,6 +1154,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); @@ -1168,6 +1321,12 @@ public class EventTypes { entityEventDetails.put(EVENT_UPDATE_IMAGE_STORE_ACCESS_STATE, ImageStore.class); entityEventDetails.put(EVENT_LIVE_PATCH_SYSTEMVM, "SystemVMs"); + entityEventDetails.put(EVENT_MS_MAINTENANCE_PREPARE, "ManagementServer"); + entityEventDetails.put(EVENT_MS_MAINTENANCE_CANCEL, "ManagementServer"); + entityEventDetails.put(EVENT_MS_SHUTDOWN_PREPARE, "ManagementServer"); + entityEventDetails.put(EVENT_MS_SHUTDOWN_CANCEL, "ManagementServer"); + entityEventDetails.put(EVENT_MS_SHUTDOWN, "ManagementServer"); + //Object Store entityEventDetails.put(EVENT_OBJECT_STORE_CREATE, ObjectStore.class); entityEventDetails.put(EVENT_OBJECT_STORE_UPDATE, ObjectStore.class); @@ -1177,8 +1336,74 @@ public class EventTypes { entityEventDetails.put(EVENT_BUCKET_CREATE, Bucket.class); entityEventDetails.put(EVENT_BUCKET_UPDATE, Bucket.class); entityEventDetails.put(EVENT_BUCKET_DELETE, Bucket.class); + + // Quota + entityEventDetails.put(EVENT_QUOTA_TARIFF_CREATE, QuotaTariff.class); + entityEventDetails.put(EVENT_QUOTA_TARIFF_DELETE, QuotaTariff.class); + entityEventDetails.put(EVENT_QUOTA_TARIFF_UPDATE, QuotaTariff.class); + + // Routing + entityEventDetails.put(EVENT_ZONE_IP4_SUBNET_CREATE, DataCenterIpv4GuestSubnet.class); + entityEventDetails.put(EVENT_ZONE_IP4_SUBNET_UPDATE, DataCenterIpv4GuestSubnet.class); + entityEventDetails.put(EVENT_ZONE_IP4_SUBNET_DELETE, DataCenterIpv4GuestSubnet.class); + entityEventDetails.put(EVENT_ZONE_IP4_SUBNET_DEDICATE, DataCenterIpv4GuestSubnet.class); + entityEventDetails.put(EVENT_ZONE_IP4_SUBNET_RELEASE, DataCenterIpv4GuestSubnet.class); + entityEventDetails.put(EVENT_IP4_GUEST_SUBNET_CREATE, Ipv4GuestSubnetNetworkMap.class); + entityEventDetails.put(EVENT_IP4_GUEST_SUBNET_DELETE, Ipv4GuestSubnetNetworkMap.class); + entityEventDetails.put(EVENT_ROUTING_IPV4_FIREWALL_RULE_CREATE, FirewallRule.class); + entityEventDetails.put(EVENT_ROUTING_IPV4_FIREWALL_RULE_UPDATE, FirewallRule.class); + entityEventDetails.put(EVENT_ROUTING_IPV4_FIREWALL_RULE_DELETE, FirewallRule.class); + entityEventDetails.put(EVENT_BGP_PEER_CREATE, BgpPeer.class); + entityEventDetails.put(EVENT_BGP_PEER_UPDATE, BgpPeer.class); + entityEventDetails.put(EVENT_BGP_PEER_DELETE, BgpPeer.class); + entityEventDetails.put(EVENT_BGP_PEER_DEDICATE, BgpPeer.class); + entityEventDetails.put(EVENT_BGP_PEER_RELEASE, BgpPeer.class); + + // SharedFS + entityEventDetails.put(EVENT_SHAREDFS_CREATE, SharedFS.class); + entityEventDetails.put(EVENT_SHAREDFS_START, SharedFS.class); + entityEventDetails.put(EVENT_SHAREDFS_STOP, SharedFS.class); + entityEventDetails.put(EVENT_SHAREDFS_UPDATE, SharedFS.class); + entityEventDetails.put(EVENT_SHAREDFS_CHANGE_SERVICE_OFFERING, SharedFS.class); + entityEventDetails.put(EVENT_SHAREDFS_CHANGE_DISK_OFFERING, SharedFS.class); + entityEventDetails.put(EVENT_SHAREDFS_RESTART, SharedFS.class); + entityEventDetails.put(EVENT_SHAREDFS_DESTROY, SharedFS.class); + entityEventDetails.put(EVENT_SHAREDFS_EXPUNGE, SharedFS.class); + entityEventDetails.put(EVENT_SHAREDFS_RECOVER, SharedFS.class); + + // Management Server + entityEventDetails.put(EVENT_MANAGEMENT_SERVER_REMOVE, "ManagementServer"); + + // VM Lease + entityEventDetails.put(VM_LEASE_EXPIRED, VirtualMachine.class); + entityEventDetails.put(VM_LEASE_EXPIRING, VirtualMachine.class); + entityEventDetails.put(VM_LEASE_DISABLED, VirtualMachine.class); + entityEventDetails.put(VM_LEASE_CANCELLED, VirtualMachine.class); + + // GUI theme + entityEventDetails.put(EVENT_GUI_THEME_CREATE, "GuiTheme"); + entityEventDetails.put(EVENT_GUI_THEME_REMOVE, "GuiTheme"); + entityEventDetails.put(EVENT_GUI_THEME_UPDATE, "GuiTheme"); + + // Extension + entityEventDetails.put(EVENT_EXTENSION_CREATE, Extension.class); + entityEventDetails.put(EVENT_EXTENSION_UPDATE, Extension.class); + entityEventDetails.put(EVENT_EXTENSION_DELETE, Extension.class); + entityEventDetails.put(EVENT_EXTENSION_RESOURCE_REGISTER, Extension.class); + entityEventDetails.put(EVENT_EXTENSION_RESOURCE_UNREGISTER, Extension.class); + entityEventDetails.put(EVENT_EXTENSION_CUSTOM_ACTION_ADD, ExtensionCustomAction.class); + entityEventDetails.put(EVENT_EXTENSION_CUSTOM_ACTION_UPDATE, ExtensionCustomAction.class); + entityEventDetails.put(EVENT_EXTENSION_CUSTOM_ACTION_DELETE, ExtensionCustomAction.class); + + // Backup Repository + entityEventDetails.put(EVENT_BACKUP_REPOSITORY_ADD, BackupRepositoryService.class); + entityEventDetails.put(EVENT_BACKUP_REPOSITORY_UPDATE, BackupRepositoryService.class); } + public static boolean isNetworkEvent(String eventType) { + return EVENT_NETWORK_CREATE.equals(eventType) || EVENT_NETWORK_DELETE.equals(eventType) || + EVENT_NETWORK_UPDATE.equals(eventType); + } public static String getEntityForEvent(String eventName) { Object entityClass = entityEventDetails.get(eventName); if (entityClass == null) { @@ -1207,4 +1432,12 @@ public static Class getEntityClassForEvent(String eventName) { return null; } + + public static boolean isVpcEvent(String eventType) { + return EventTypes.EVENT_VPC_CREATE.equals(eventType) || EventTypes.EVENT_VPC_DELETE.equals(eventType); + } + + public static void addEntityEventDetail(String event, Class clazz) { + entityEventDetails.put(event, clazz); + } } diff --git a/api/src/main/java/com/cloud/exception/OperationTimedoutException.java b/api/src/main/java/com/cloud/exception/OperationTimedoutException.java index fe27408eb4e3..66b607100d97 100644 --- a/api/src/main/java/com/cloud/exception/OperationTimedoutException.java +++ b/api/src/main/java/com/cloud/exception/OperationTimedoutException.java @@ -40,7 +40,7 @@ public class OperationTimedoutException extends CloudException { boolean _isActive; public OperationTimedoutException(Command[] cmds, long agentId, long seqId, int time, boolean isActive) { - super("Commands " + seqId + " to Host " + agentId + " timed out after " + time); + super("Commands " + seqId + " to Host " + agentId + " timed out after " + time + " secs"); _agentId = agentId; _seqId = seqId; _time = time; diff --git a/api/src/main/java/com/cloud/exception/StorageAccessException.java b/api/src/main/java/com/cloud/exception/StorageAccessException.java index eefbcf5518a3..d54d77d66f1e 100644 --- a/api/src/main/java/com/cloud/exception/StorageAccessException.java +++ b/api/src/main/java/com/cloud/exception/StorageAccessException.java @@ -26,7 +26,7 @@ public class StorageAccessException extends RuntimeException { private static final long serialVersionUID = SerialVersionUID.StorageAccessException; - public StorageAccessException(String message) { - super(message); + public StorageAccessException(String message, Exception causer) { + super(message, causer); } } diff --git a/api/src/main/java/com/cloud/host/Host.java b/api/src/main/java/com/cloud/host/Host.java index 7563bc3b7426..9c011bac3190 100644 --- a/api/src/main/java/com/cloud/host/Host.java +++ b/api/src/main/java/com/cloud/host/Host.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.host; +import com.cloud.cpu.CPU; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.resource.ResourceState; import com.cloud.utils.fsm.StateObject; @@ -52,8 +53,15 @@ public static String[] toStrings(Host.Type... types) { return strs; } } - public static final String HOST_UEFI_ENABLE = "host.uefi.enable"; - public static final String HOST_VOLUME_ENCRYPTION = "host.volume.encryption"; + + String HOST_UEFI_ENABLE = "host.uefi.enable"; + String HOST_VOLUME_ENCRYPTION = "host.volume.encryption"; + String HOST_INSTANCE_CONVERSION = "host.instance.conversion"; + String HOST_OVFTOOL_VERSION = "host.ovftool.version"; + String HOST_VIRTV2V_VERSION = "host.virtv2v.version"; + String HOST_SSH_PORT = "host.ssh.port"; + + int DEFAULT_SSH_PORT = 22; /** * @return name of the machine. @@ -175,6 +183,8 @@ public static String[] toStrings(Host.Type... types) { */ Long getManagementServerId(); + Long getLastManagementServerId(); + /* *@return removal date */ @@ -207,4 +217,8 @@ public static String[] toStrings(Host.Type... types) { boolean isDisabled(); ResourceState getResourceState(); + + CPU.CPUArch getArch(); + + String getStorageAccessGroups(); } 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/host/Status.java b/api/src/main/java/com/cloud/host/Status.java index 5dc82bbfaefa..af6af82e9739 100644 --- a/api/src/main/java/com/cloud/host/Status.java +++ b/api/src/main/java/com/cloud/host/Status.java @@ -127,6 +127,7 @@ public static String[] toStrings(Status... states) { s_fsm.addTransition(Status.Connecting, Event.HostDown, Status.Down); s_fsm.addTransition(Status.Connecting, Event.Ping, Status.Connecting); s_fsm.addTransition(Status.Connecting, Event.ManagementServerDown, Status.Disconnected); + s_fsm.addTransition(Status.Connecting, Event.StartAgentRebalance, Status.Rebalancing); s_fsm.addTransition(Status.Connecting, Event.AgentDisconnected, Status.Alert); s_fsm.addTransition(Status.Up, Event.PingTimeout, Status.Alert); s_fsm.addTransition(Status.Up, Event.AgentDisconnected, Status.Alert); diff --git a/api/src/main/java/com/cloud/hypervisor/Hypervisor.java b/api/src/main/java/com/cloud/hypervisor/Hypervisor.java index 2f0cc736af3d..1f8741d3b7b2 100644 --- a/api/src/main/java/com/cloud/hypervisor/Hypervisor.java +++ b/api/src/main/java/com/cloud/hypervisor/Hypervisor.java @@ -17,55 +17,68 @@ package com.cloud.hypervisor; import com.cloud.storage.Storage.ImageFormat; +import org.apache.commons.lang3.StringUtils; -import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.EnumSet; +import java.util.stream.Collectors; + +import static com.cloud.hypervisor.Hypervisor.HypervisorType.Functionality.DirectDownloadTemplate; +import static com.cloud.hypervisor.Hypervisor.HypervisorType.Functionality.RootDiskSizeOverride; +import static com.cloud.hypervisor.Hypervisor.HypervisorType.Functionality.VmStorageMigration; +import static com.cloud.hypervisor.Hypervisor.HypervisorType.Functionality.VmStorageMigrationWithSnapshots; public class Hypervisor { + public static class HypervisorType { + public enum Functionality { + DirectDownloadTemplate, + RootDiskSizeOverride, + VmStorageMigration, + VmStorageMigrationWithSnapshots + } + + private static final Map hypervisorTypeMap = new LinkedHashMap<>(); + public static final HypervisorType None = new HypervisorType("None"); //for storage hosts + public static final HypervisorType XenServer = new HypervisorType("XenServer", ImageFormat.VHD, EnumSet.of(RootDiskSizeOverride, VmStorageMigration)); + public static final HypervisorType KVM = new HypervisorType("KVM", ImageFormat.QCOW2, EnumSet.of(DirectDownloadTemplate, RootDiskSizeOverride, VmStorageMigration)); + public static final HypervisorType VMware = new HypervisorType("VMware", ImageFormat.OVA, EnumSet.of(RootDiskSizeOverride, VmStorageMigration, VmStorageMigrationWithSnapshots)); + public static final HypervisorType Hyperv = new HypervisorType("Hyperv"); + public static final HypervisorType VirtualBox = new HypervisorType("VirtualBox"); + public static final HypervisorType Parralels = new HypervisorType("Parralels"); + public static final HypervisorType BareMetal = new HypervisorType("BareMetal"); + public static final HypervisorType Simulator = new HypervisorType("Simulator", null, EnumSet.of(RootDiskSizeOverride, VmStorageMigration)); + public static final HypervisorType Ovm = new HypervisorType("Ovm", ImageFormat.RAW); + public static final HypervisorType Ovm3 = new HypervisorType("Ovm3", ImageFormat.RAW); + public static final HypervisorType LXC = new HypervisorType("LXC"); + public static final HypervisorType Custom = new HypervisorType("Custom", null, EnumSet.of(RootDiskSizeOverride)); + public static final HypervisorType External = new HypervisorType("External", null, EnumSet.of(RootDiskSizeOverride)); + public static final HypervisorType Any = new HypervisorType("Any"); /*If you don't care about the hypervisor type*/ + private final String name; + private final ImageFormat imageFormat; + private final Set supportedFunctionalities; + + public HypervisorType(String name) { + this(name, null, EnumSet.noneOf(Functionality.class)); + } + + public HypervisorType(String name, ImageFormat imageFormat) { + this(name, imageFormat, EnumSet.noneOf(Functionality.class)); + } - static Map hypervisorTypeMap; - static Map supportedImageFormatMap; - - public enum HypervisorType { - None, //for storage hosts - XenServer, - KVM, - VMware, - Hyperv, - VirtualBox, - Parralels, - BareMetal, - Simulator, - Ovm, - Ovm3, - LXC, - Custom, - - Any; /*If you don't care about the hypervisor type*/ - - static { - hypervisorTypeMap = new HashMap<>(); - hypervisorTypeMap.put("xenserver", HypervisorType.XenServer); - hypervisorTypeMap.put("kvm", HypervisorType.KVM); - hypervisorTypeMap.put("vmware", HypervisorType.VMware); - hypervisorTypeMap.put("hyperv", HypervisorType.Hyperv); - hypervisorTypeMap.put("virtualbox", HypervisorType.VirtualBox); - hypervisorTypeMap.put("parallels", HypervisorType.Parralels); - hypervisorTypeMap.put("baremetal", HypervisorType.BareMetal); - hypervisorTypeMap.put("simulator", HypervisorType.Simulator); - hypervisorTypeMap.put("ovm", HypervisorType.Ovm); - hypervisorTypeMap.put("lxc", HypervisorType.LXC); - hypervisorTypeMap.put("any", HypervisorType.Any); - hypervisorTypeMap.put("ovm3", HypervisorType.Ovm3); - hypervisorTypeMap.put("custom", HypervisorType.Custom); - - supportedImageFormatMap = new HashMap<>(); - supportedImageFormatMap.put(HypervisorType.XenServer, ImageFormat.VHD); - supportedImageFormatMap.put(HypervisorType.KVM, ImageFormat.QCOW2); - supportedImageFormatMap.put(HypervisorType.VMware, ImageFormat.OVA); - supportedImageFormatMap.put(HypervisorType.Ovm, ImageFormat.RAW); - supportedImageFormatMap.put(HypervisorType.Ovm3, ImageFormat.RAW); + public HypervisorType(String name, ImageFormat imageFormat, Set supportedFunctionalities) { + this.name = name; + this.imageFormat = imageFormat; + this.supportedFunctionalities = supportedFunctionalities; + if (name.equals("Parralels")){ // typo in the original code + hypervisorTypeMap.put("parallels", this); + } else { + hypervisorTypeMap.putIfAbsent(name.toLowerCase(Locale.ROOT), this); + } } public static HypervisorType getType(String hypervisor) { @@ -75,24 +88,77 @@ public static HypervisorType getType(String hypervisor) { hypervisorTypeMap.getOrDefault(hypervisor.toLowerCase(Locale.ROOT), HypervisorType.None)); } + public static HypervisorType[] values() { + return hypervisorTypeMap.values().toArray(HypervisorType[]::new).clone(); + } + + public static HypervisorType valueOf(String name) { + if (StringUtils.isBlank(name)) { + return null; + } + + HypervisorType hypervisorType = hypervisorTypeMap.get(name.toLowerCase(Locale.ROOT)); + if (hypervisorType == null) { + throw new IllegalArgumentException("HypervisorType '" + name + "' not found"); + } + return hypervisorType; + } + + public static List getListOfHypervisorsSupportingFunctionality(Functionality functionality) { + return hypervisorTypeMap.values().stream() + .filter(hypervisor -> hypervisor.supportedFunctionalities.contains(functionality)) + .collect(Collectors.toList()); + } + /** * Returns the display name of a hypervisor type in case the custom hypervisor is used, * using the 'hypervisor.custom.display.name' setting. Otherwise, returns hypervisor name */ public String getHypervisorDisplayName() { - return !Hypervisor.HypervisorType.Custom.equals(this) ? - this.toString() : - HypervisorGuru.HypervisorCustomDisplayName.value(); + return HypervisorType.Custom.equals(this) ? HypervisorGuru.HypervisorCustomDisplayName.value() : name; } /** * This method really needs to be part of the properties of the hypervisor type itself. * - * @param hyperType * @return */ - public static ImageFormat getSupportedImageFormat(HypervisorType hyperType) { - return supportedImageFormatMap.getOrDefault(hyperType, null); + public ImageFormat getSupportedImageFormat() { + return imageFormat; + } + + public String name() { + return name; + } + + /** + * Make this method to be part of the properties of the hypervisor type itself. + * + * @return true if the hypervisor plugin support the specified functionality + */ + public boolean isFunctionalitySupported(Functionality functionality) { + return supportedFunctionalities.contains(functionality); + } + + @Override + public int hashCode() { + return Objects.hash(name); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } else if (o == null || getClass() != o.getClass()) { + return false; + } + HypervisorType that = (HypervisorType) o; + return Objects.equals(name, that.name); + } + + @Override + public String toString() { + return name; } } diff --git a/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java b/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java index 3c7dbac6442c..0c821b4e36c0 100644 --- a/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java +++ b/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java @@ -23,6 +23,7 @@ import org.apache.cloudstack.framework.config.ConfigKey; import com.cloud.agent.api.Command; +import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.hypervisor.Hypervisor.HypervisorType; @@ -101,21 +102,20 @@ boolean attachRestoredVolumeToVirtualMachine(long zoneId, String location, Backu * Will generate commands to migrate a vm to a pool. For now this will only work for stopped VMs on Vmware. * * @param vm the stopped vm to migrate - * @param destination the primary storage pool to migrate to + * @param volumeToPool the primary storage pools to migrate to * @return a list of commands to perform for a successful migration */ List finalizeMigrate(VirtualMachine vm, Map volumeToPool); /** - * Will perform a clone of a VM on an external host (if the guru can handle) + * Will return the hypervisor VM (clone VM for PowerOn VMs), performs a clone of a VM if required on an external host (if the guru can handle) * @param hostIp VM's source host IP - * @param vmName name of the source VM to clone from + * @param vmName name of the source VM (clone VM name if cloned) * @param params hypervisor specific additional parameters - * @return a reference to the cloned VM + * @return a reference to the hypervisor or cloned VM, and cloned flag */ - UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmName, - Map params); + Pair getHypervisorVMOutOfBandAndCloneIfRequired(String hostIp, String vmName, Map params); /** * Removes a VM created as a clone of a VM on an external host @@ -124,6 +124,23 @@ UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmName, * @param params hypervisor specific additional parameters * @return true if the operation succeeds, false if not */ - boolean removeClonedHypervisorVMOutOfBand(String hostIp, String vmName, - Map params); + boolean removeClonedHypervisorVMOutOfBand(String hostIp, String vmName, Map params); + + /** + * Create an OVA/OVF template of a VM on an external host (if the guru can handle) + * @param hostIp VM's source host IP + * @param vmName name of the source VM to create template from + * @param params hypervisor specific additional parameters + * @param templateLocation datastore to create the template file + * @return the created template dir/name + */ + String createVMTemplateOutOfBand(String hostIp, String vmName, Map params, DataStoreTO templateLocation, int threadsCountToExportOvf); + + /** + * Removes the template on the location + * @param templateLocation datastore to remove the template file + * @param templateDir the template dir to remove from datastore + * @return true if the operation succeeds, false if not + */ + boolean removeVMTemplateOutOfBand(DataStoreTO templateLocation, String templateDir); } diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java similarity index 76% rename from plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java rename to api/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java index 591da077aec6..80f6a6045c72 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java +++ b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java @@ -44,6 +44,8 @@ enum Event { AutoscaleRequested, ScaleUpRequested, ScaleDownRequested, + AddNodeRequested, + RemoveNodeRequested, UpgradeRequested, OperationSucceeded, OperationFailed, @@ -58,7 +60,10 @@ enum State { Stopping("Resources for the Kubernetes cluster are being destroyed"), Stopped("All resources for the Kubernetes cluster are destroyed, Kubernetes cluster may still have ephemeral resource like persistent volumes provisioned"), Scaling("Transient state in which resources are either getting scaled up/down"), + ScalingStoppedCluster("Transient state in which the service offerings of stopped clusters are getting scaled"), Upgrading("Transient state in which cluster is getting upgraded"), + Importing("Transient state in which additional nodes are added as worker nodes to a cluster"), + RemovingNodes("Transient state in which additional nodes are removed from a cluster"), Alert("State to represent Kubernetes clusters which are not in expected desired state (operationally in active control place, stopped cluster VM's etc)."), Recovering("State in which Kubernetes cluster is recovering from alert state"), Destroyed("End state of Kubernetes cluster in which all resources are destroyed, cluster will not be usable further"), @@ -83,19 +88,35 @@ 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); s_fsm.addTransition(State.Running, Event.AutoscaleRequested, State.Scaling); s_fsm.addTransition(State.Running, Event.ScaleUpRequested, State.Scaling); s_fsm.addTransition(State.Running, Event.ScaleDownRequested, State.Scaling); + s_fsm.addTransition(State.Stopped, Event.ScaleUpRequested, State.ScalingStoppedCluster); s_fsm.addTransition(State.Scaling, Event.OperationSucceeded, State.Running); - s_fsm.addTransition(State.Scaling, Event.OperationFailed, State.Alert); + s_fsm.addTransition(State.Scaling, Event.OperationFailed, State.Running); + s_fsm.addTransition(State.ScalingStoppedCluster, Event.OperationSucceeded, State.Stopped); + s_fsm.addTransition(State.ScalingStoppedCluster, Event.OperationFailed, State.Alert); s_fsm.addTransition(State.Running, Event.UpgradeRequested, State.Upgrading); s_fsm.addTransition(State.Upgrading, Event.OperationSucceeded, State.Running); s_fsm.addTransition(State.Upgrading, Event.OperationFailed, State.Alert); + s_fsm.addTransition(State.Running, Event.AddNodeRequested, State.Importing); + s_fsm.addTransition(State.Alert, Event.AddNodeRequested, State.Importing); + s_fsm.addTransition(State.Importing, Event.OperationSucceeded, State.Running); + s_fsm.addTransition(State.Importing, Event.OperationFailed, State.Running); + s_fsm.addTransition(State.Alert, Event.OperationSucceeded, State.Running); + + s_fsm.addTransition(State.Running, Event.RemoveNodeRequested, State.RemovingNodes); + s_fsm.addTransition(State.Alert, Event.RemoveNodeRequested, State.RemovingNodes); + s_fsm.addTransition(State.RemovingNodes, Event.OperationSucceeded, State.Running); + s_fsm.addTransition(State.RemovingNodes, Event.OperationFailed, State.Running); + s_fsm.addTransition(State.Alert, Event.RecoveryRequested, State.Recovering); s_fsm.addTransition(State.Recovering, Event.OperationSucceeded, State.Running); s_fsm.addTransition(State.Recovering, Event.OperationFailed, State.Alert); @@ -142,4 +163,14 @@ enum State { Long getMaxSize(); Long getSecurityGroupId(); ClusterType getClusterType(); + Long getControlNodeServiceOfferingId(); + Long getWorkerNodeServiceOfferingId(); + Long getEtcdNodeServiceOfferingId(); + Long getControlNodeTemplateId(); + Long getWorkerNodeTemplateId(); + Long getEtcdNodeTemplateId(); + Long getEtcdNodeCount(); + Long getCniConfigId(); + String getCniConfigDetails(); + boolean isCsiEnabled(); } diff --git a/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelper.java b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelper.java deleted file mode 100644 index e445e50f82cb..000000000000 --- a/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelper.java +++ /dev/null @@ -1,25 +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 com.cloud.kubernetes.cluster; - -import com.cloud.utils.component.Adapter; -import org.apache.cloudstack.acl.ControlledEntity; - -public interface KubernetesClusterHelper extends Adapter { - - ControlledEntity findByUuid(String uuid); -} diff --git a/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesServiceHelper.java b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesServiceHelper.java new file mode 100644 index 000000000000..5a6eaa3f7b9a --- /dev/null +++ b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesServiceHelper.java @@ -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. +package com.cloud.kubernetes.cluster; + +import org.apache.cloudstack.acl.ControlledEntity; + +import java.util.List; +import java.util.Map; + +import com.cloud.user.Account; +import com.cloud.uservm.UserVm; +import com.cloud.utils.component.Adapter; + +public interface KubernetesServiceHelper extends Adapter { + + enum KubernetesClusterNodeType { + CONTROL, WORKER, ETCD, DEFAULT + } + + ControlledEntity findByUuid(String uuid); + ControlledEntity findByVmId(long vmId); + void checkVmCanBeDestroyed(UserVm userVm); + 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/IpAddress.java b/api/src/main/java/com/cloud/network/IpAddress.java index cf2e2f82db9f..70d652b54e99 100644 --- a/api/src/main/java/com/cloud/network/IpAddress.java +++ b/api/src/main/java/com/cloud/network/IpAddress.java @@ -97,4 +97,7 @@ enum Purpose { void setRuleState(State ruleState); + boolean isForSystemVms(); + + boolean isForRouter(); } diff --git a/api/src/main/java/com/cloud/network/Ipv6Service.java b/api/src/main/java/com/cloud/network/Ipv6Service.java index 2b4dff01086e..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, @@ -58,7 +58,7 @@ public interface Ipv6Service extends PluggableService, Configurable { Pair getUsedTotalIpv6SubnetForZone(long zoneId); - Pair preAllocateIpv6SubnetForNetwork(long zoneId) throws ResourceAllocationException; + Pair preAllocateIpv6SubnetForNetwork(DataCenter zone) throws ResourceAllocationException; void assignIpv6SubnetToNetwork(String subnet, long networkId); diff --git a/api/src/main/java/com/cloud/network/Network.java b/api/src/main/java/com/cloud/network/Network.java index 458169c80aa3..0846306f70f9 100644 --- a/api/src/main/java/com/cloud/network/Network.java +++ b/api/src/main/java/com/cloud/network/Network.java @@ -103,7 +103,7 @@ class Service { public static final Service Vpn = new Service("Vpn", Capability.SupportedVpnProtocols, Capability.VpnTypes); public static final Service Dhcp = new Service("Dhcp", Capability.ExtraDhcpOptions); public static final Service Dns = new Service("Dns", Capability.AllowDnsSuffixModification); - public static final Service Gateway = new Service("Gateway"); + public static final Service Gateway = new Service("Gateway", Capability.RedundantRouter); public static final Service Firewall = new Service("Firewall", Capability.SupportedProtocols, Capability.MultipleIps, Capability.TrafficStatistics, Capability.SupportedTrafficDirection, Capability.SupportedEgressProtocols); public static final Service Lb = new Service("Lb", Capability.SupportedLBAlgorithms, Capability.SupportedLBIsolation, Capability.SupportedProtocols, @@ -205,6 +205,9 @@ public static class Provider { //Add Tungsten Fabric provider public static final Provider Tungsten = new Provider("Tungsten", false); + public static final Provider Nsx = new Provider("Nsx", false); + public static final Provider Netris = new Provider("Netris", false); + private final String name; private final boolean isExternal; @@ -322,9 +325,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(); @@ -410,12 +413,16 @@ public void setIp6Address(String ip6Address) { String getGateway(); + void setGateway(String gateway); + // "cidr" is the Cloudstack managed address space, all CloudStack managed vms get IP address from "cidr", // In general "cidr" also serves as the network CIDR // But in case IP reservation is configured for a Guest network, "networkcidr" is the Effective network CIDR for that network, // "cidr" will still continue to be the effective address space for CloudStack managed vms in that Guest network String getCidr(); + void setCidr(String cidr); + // "networkcidr" is the network CIDR of the guest network which uses IP reservation. // It is the summation of "cidr" and the reservedIPrange(the address space used for non CloudStack purposes). // For networks not configured with IP reservation, "networkcidr" is always null @@ -427,6 +434,8 @@ public void setIp6Address(String ip6Address) { long getDataCenterId(); + long getAccountId(); + long getNetworkOfferingId(); @Override @@ -499,4 +508,6 @@ public void setIp6Address(String ip6Address) { Integer getPublicMtu(); Integer getPrivateMtu(); + + Integer getNetworkCidrSize(); } diff --git a/api/src/main/java/com/cloud/network/NetworkModel.java b/api/src/main/java/com/cloud/network/NetworkModel.java index 53ac735cf050..c212e6319eb4 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); @@ -149,7 +153,7 @@ public interface NetworkModel { boolean areServicesSupportedByNetworkOffering(long networkOfferingId, Service... services); - Network getNetworkWithSGWithFreeIPs(Long zoneId); + Network getNetworkWithSGWithFreeIPs(Account account, Long zoneId); Network getNetworkWithSecurityGroupEnabled(Long zoneId); @@ -173,6 +177,8 @@ public interface NetworkModel { boolean isProviderSupportServiceInNetwork(long networkId, Service service, Provider provider); + boolean isAnyServiceSupportedInNetwork(long networkId, Provider provider, Service... services); + boolean isProviderEnabledInPhysicalNetwork(long physicalNetowrkId, String providerName); String getNetworkTag(HypervisorType hType, Network network); @@ -303,6 +309,8 @@ public interface NetworkModel { NicProfile getNicProfile(VirtualMachine vm, long networkId, String broadcastUri); + NicProfile getNicProfile(VirtualMachine vm, Nic nic, DataCenter dataCenter); + Set getAvailableIps(Network network, String requestedIp); String getDomainNetworkDomain(long domainId, long zoneId); @@ -317,6 +325,8 @@ public interface NetworkModel { void checkIp6Parameters(String startIPv6, String endIPv6, String ip6Gateway, String ip6Cidr) throws InvalidParameterValueException; + void checkIp6CidrSizeEqualTo64(String ip6Cidr) throws InvalidParameterValueException; + void checkRequestedIpAddresses(long networkId, IpAddresses ips) throws InvalidParameterValueException; String getStartIpv6Address(long id); @@ -354,4 +364,12 @@ List generateVmData(String userData, String userDataDetails, String se void verifyIp6DnsPair(final String ip6Dns1, final String ip6Dns2); + boolean isSecurityGroupSupportedForZone(Long zoneId); + + 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 1a5c80ea8718..2e8efb489308 100644 --- a/api/src/main/java/com/cloud/network/NetworkProfile.java +++ b/api/src/main/java/com/cloud/network/NetworkProfile.java @@ -22,6 +22,7 @@ import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.Networks.Mode; import com.cloud.network.Networks.TrafficType; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; public class NetworkProfile implements Network { private final long id; @@ -41,8 +42,8 @@ public class NetworkProfile implements Network { private final Mode mode; private final BroadcastDomainType broadcastDomainType; private TrafficType trafficType; - private final String gateway; - private final String cidr; + private String gateway; + private String cidr; private final String networkCidr; private final String ip6Gateway; private final String ip6Cidr; @@ -62,6 +63,7 @@ public class NetworkProfile implements Network { private final String guruName; private boolean strechedL2Subnet; private String externalId; + private Integer networkCidrSize; public NetworkProfile(Network network) { id = network.getId(); @@ -98,6 +100,7 @@ public NetworkProfile(Network network) { isRedundant = network.isRedundant(); isRollingRestart = network.isRollingRestart(); externalId = network.getExternalId(); + networkCidrSize = network.getNetworkCidrSize(); } @Override @@ -210,11 +213,21 @@ public String getGateway() { return gateway; } + @Override + public void setGateway(String gateway) { + this.gateway = gateway; + } + @Override public String getCidr() { return cidr; } + @Override + public void setCidr(String cidr) { + this.cidr = cidr; + } + @Override public String getNetworkCidr() { return networkCidr; @@ -367,4 +380,16 @@ public Integer getPrivateMtu() { return null; } + @Override + public Integer getNetworkCidrSize() { + return networkCidrSize; + } + + @Override + public String toString() { + return String.format("NetworkProfile %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "networkOfferingId")); + } + } diff --git a/api/src/main/java/com/cloud/network/NetworkService.java b/api/src/main/java/com/cloud/network/NetworkService.java index 82d229da459d..742206c7e3bf 100644 --- a/api/src/main/java/com/cloud/network/NetworkService.java +++ b/api/src/main/java/com/cloud/network/NetworkService.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Map; +import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.api.command.admin.address.ReleasePodIpCmdByAdmin; import org.apache.cloudstack.api.command.admin.network.DedicateGuestVlanRangeCmd; import org.apache.cloudstack.api.command.admin.network.ListDedicatedGuestVlanRangesCmd; @@ -37,13 +38,16 @@ import org.apache.cloudstack.api.command.user.vm.ListNicsCmd; import org.apache.cloudstack.api.response.AcquirePodIpCmdResponse; import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.network.element.InternalLoadBalancerElementService; +import com.cloud.agent.api.to.NicTO; +import com.cloud.dc.DataCenter; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientAddressCapacityException; import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; -import com.cloud.exception.InvalidParameterValueException; import com.cloud.network.Network.IpAddresses; import com.cloud.network.Network.Service; import com.cloud.network.Networks.TrafficType; @@ -77,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); @@ -87,6 +91,8 @@ IpAddress allocateIP(Account ipOwner, long zoneId, Long networkId, Boolean displ IpAddress reserveIpAddress(Account account, Boolean displayIp, Long ipAddressId) throws ResourceAllocationException; + IpAddress reserveIpAddressWithVlanDetail(Account account, DataCenter zone, Boolean displayIp, String vlanDetailKey) throws ResourceAllocationException; + boolean releaseReservedIpAddress(long ipAddressId) throws InsufficientAddressCapacityException; boolean releaseIpAddress(long ipAddressId) throws InsufficientAddressCapacityException; @@ -98,6 +104,14 @@ IpAddress allocatePortableIP(Account ipOwner, int regionId, Long zoneId, Long ne Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapacityException, ConcurrentOperationException, ResourceAllocationException; + Network createGuestNetwork(long networkOfferingId, String name, String displayText, Account owner, + 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); @@ -254,4 +268,15 @@ Network createPrivateNetwork(String networkName, String displayText, long physic PublicIpQuarantine updatePublicIpAddressInQuarantine(UpdateQuarantinedIpCmd cmd); void removePublicIpAddressFromQuarantine(RemoveQuarantinedIpCmd cmd); + + InternalLoadBalancerElementService getInternalLoadBalancerElementByType(VirtualRouterProvider.Type type); + InternalLoadBalancerElementService getInternalLoadBalancerElementByNetworkServiceProviderId(long networkProviderId); + InternalLoadBalancerElementService getInternalLoadBalancerElementById(long providerId); + List getInternalLoadBalancerElements(); + + boolean handleCksIsoOnNetworkVirtualRouter(Long virtualRouterId, boolean mount) throws ResourceUnavailableException; + + IpAddresses getIpAddressesFromIps(String ipAddress, String ip6Address, String macAddress); + + String getNicVlanValueForExternalVm(NicTO nic); } diff --git a/api/src/main/java/com/cloud/network/Networks.java b/api/src/main/java/com/cloud/network/Networks.java index aeed5d4aec6a..5f767686dc97 100644 --- a/api/src/main/java/com/cloud/network/Networks.java +++ b/api/src/main/java/com/cloud/network/Networks.java @@ -78,7 +78,7 @@ 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) { @@ -96,7 +96,7 @@ 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), @@ -128,7 +128,9 @@ public URI toUri(T value) { }, UnDecided(null, null), OpenDaylight("opendaylight", String.class), - TUNGSTEN("tf", String.class); + TUNGSTEN("tf", String.class), + NSX("nsx", String.class), + Netris("netris", String.class); private final String scheme; private final Class type; @@ -175,7 +177,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()); } /** @@ -189,7 +191,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)); } /** @@ -218,7 +220,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(); } /** @@ -241,7 +243,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/SDNProviderNetworkRule.java b/api/src/main/java/com/cloud/network/SDNProviderNetworkRule.java new file mode 100644 index 000000000000..a22db4287dcd --- /dev/null +++ b/api/src/main/java/com/cloud/network/SDNProviderNetworkRule.java @@ -0,0 +1,358 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network; + +import java.util.List; + +public class SDNProviderNetworkRule { + + protected long domainId; + protected long accountId; + protected long zoneId; + protected Long networkResourceId; + protected String networkResourceName; + protected boolean isVpcResource; + protected long vmId; + protected long ruleId; + protected String publicIp; + protected String vmIp; + protected String publicPort; + protected String privatePort; + protected String protocol; + protected String algorithm; + protected List sourceCidrList; + protected List destinationCidrList; + protected Integer icmpCode; + + protected Integer icmpType; + protected String trafficType; + protected Network.Service service; + + public long getDomainId() { + return domainId; + } + + public void setDomainId(long domainId) { + this.domainId = domainId; + } + + public long getAccountId() { + return accountId; + } + + public void setAccountId(long accountId) { + this.accountId = accountId; + } + + public long getZoneId() { + return zoneId; + } + + public void setZoneId(long zoneId) { + this.zoneId = zoneId; + } + + public Long getNetworkResourceId() { + return networkResourceId; + } + + public void setNetworkResourceId(Long networkResourceId) { + this.networkResourceId = networkResourceId; + } + + public String getNetworkResourceName() { + return networkResourceName; + } + + public void setNetworkResourceName(String networkResourceName) { + this.networkResourceName = networkResourceName; + } + + public boolean isVpcResource() { + return isVpcResource; + } + + public void setVpcResource(boolean vpcResource) { + isVpcResource = vpcResource; + } + + public long getVmId() { + return vmId; + } + + public void setVmId(long vmId) { + this.vmId = vmId; + } + + public long getRuleId() { + return ruleId; + } + + public void setRuleId(long ruleId) { + this.ruleId = ruleId; + } + + public String getPublicIp() { + return publicIp; + } + + public void setPublicIp(String publicIp) { + this.publicIp = publicIp; + } + + public String getVmIp() { + return vmIp; + } + + public void setVmIp(String vmIp) { + this.vmIp = vmIp; + } + + public String getPublicPort() { + return publicPort; + } + + public void setPublicPort(String publicPort) { + this.publicPort = publicPort; + } + + public String getPrivatePort() { + return privatePort; + } + + public void setPrivatePort(String privatePort) { + this.privatePort = privatePort; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public void setAlgorithm(String algorithm) { + this.algorithm = algorithm; + } + + public String getAlgorithm() { + return algorithm; + } + + public Network.Service getService() { + return service; + } + + public void setService(Network.Service service) { + this.service = service; + } + + public Integer getIcmpCode() { + return icmpCode; + } + + public void setIcmpCode(Integer icmpCode) { + this.icmpCode = icmpCode; + } + + public Integer getIcmpType() { + return icmpType; + } + + public void setIcmpType(Integer icmpType) { + this.icmpType = icmpType; + } + + public List getSourceCidrList() { + return sourceCidrList; + } + + public void setSourceCidrList(List sourceCidrList) { + this.sourceCidrList = sourceCidrList; + } + + public List getDestinationCidrList() { + return destinationCidrList; + } + + public void setDestinationCidrList(List destinationCidrList) { + this.destinationCidrList = destinationCidrList; + } + + public String getTrafficType() { + return trafficType; + } + + public void setTrafficType(String trafficType) { + this.trafficType = trafficType; + } + + public static class Builder { + public long domainId; + public long accountId; + public long zoneId; + public Long networkResourceId; + public String networkResourceName; + public boolean isVpcResource; + public long vmId; + + public long ruleId; + public String publicIp; + public String vmIp; + public String publicPort; + public String privatePort; + public String protocol; + public String algorithm; + public List sourceCidrList; + public List destinationCidrList; + public String trafficType; + public Integer icmpType; + public Integer icmpCode; + public Network.Service service; + + public Builder() { + // Default constructor + } + + public Builder setDomainId(long domainId) { + this.domainId = domainId; + return this; + } + + public Builder setAccountId(long accountId) { + this.accountId = accountId; + return this; + } + + public Builder setZoneId(long zoneId) { + this.zoneId = zoneId; + return this; + } + + public Builder setNetworkResourceId(Long networkResourceId) { + this.networkResourceId = networkResourceId; + return this; + } + + public Builder setNetworkResourceName(String networkResourceName) { + this.networkResourceName = networkResourceName; + return this; + } + + public Builder setVpcResource(boolean isVpcResource) { + this.isVpcResource = isVpcResource; + return this; + } + + + public Builder setVmId(long vmId) { + this.vmId = vmId; + return this; + } + + public Builder setRuleId(long ruleId) { + this.ruleId = ruleId; + return this; + } + + public Builder setPublicIp(String publicIp) { + this.publicIp = publicIp; + return this; + } + + public Builder setVmIp(String vmIp) { + this.vmIp = vmIp; + return this; + } + + public Builder setPublicPort(String publicPort) { + this.publicPort = publicPort; + return this; + } + + public Builder setPrivatePort(String privatePort) { + this.privatePort = privatePort; + return this; + } + + public Builder setProtocol(String protocol) { + this.protocol = protocol; + return this; + } + + public Builder setAlgorithm(String algorithm) { + this.algorithm = algorithm; + return this; + } + + public Builder setTrafficType(String trafficType) { + this.trafficType = trafficType; + return this; + } + + public Builder setIcmpType(Integer icmpType) { + this.icmpType = icmpType; + return this; + } + + public Builder setIcmpCode(Integer icmpCode) { + this.icmpCode = icmpCode; + return this; + } + + public Builder setSourceCidrList(List sourceCidrList) { + this.sourceCidrList = sourceCidrList; + return this; + } + + public Builder setDestinationCidrList(List destinationCidrList) { + this.destinationCidrList = destinationCidrList; + return this; + } + + public Builder setService(Network.Service service) { + this.service = service; + return this; + } + + public SDNProviderNetworkRule build() { + SDNProviderNetworkRule rule = new SDNProviderNetworkRule(); + rule.setDomainId(this.domainId); + rule.setAccountId(this.accountId); + rule.setZoneId(this.zoneId); + rule.setNetworkResourceId(this.networkResourceId); + rule.setNetworkResourceName(this.networkResourceName); + rule.setVpcResource(this.isVpcResource); + rule.setVmId(this.vmId); + rule.setVmIp(this.vmIp); + rule.setPublicIp(this.publicIp); + rule.setPublicPort(this.publicPort); + rule.setPrivatePort(this.privatePort); + rule.setProtocol(this.protocol); + rule.setRuleId(this.ruleId); + rule.setAlgorithm(this.algorithm); + rule.setIcmpType(this.icmpType); + rule.setIcmpCode(this.icmpCode); + rule.setSourceCidrList(this.sourceCidrList); + rule.setDestinationCidrList(this.destinationCidrList); + rule.setTrafficType(this.trafficType); + rule.setService(service); + return rule; + } + } +} diff --git a/api/src/main/java/com/cloud/network/Site2SiteVpnConnection.java b/api/src/main/java/com/cloud/network/Site2SiteVpnConnection.java index 994df875f7d3..51036abe0609 100644 --- a/api/src/main/java/com/cloud/network/Site2SiteVpnConnection.java +++ b/api/src/main/java/com/cloud/network/Site2SiteVpnConnection.java @@ -24,7 +24,7 @@ public interface Site2SiteVpnConnection extends ControlledEntity, InternalIdentity, Displayable { enum State { - Pending, Connecting, Connected, Disconnected, Error, + Pending, Connecting, Connected, Disconnected, Error, Removed } @Override diff --git a/api/src/main/java/com/cloud/network/VirtualNetworkApplianceService.java b/api/src/main/java/com/cloud/network/VirtualNetworkApplianceService.java index c47500c78495..a60f1d49336a 100644 --- a/api/src/main/java/com/cloud/network/VirtualNetworkApplianceService.java +++ b/api/src/main/java/com/cloud/network/VirtualNetworkApplianceService.java @@ -17,17 +17,22 @@ package com.cloud.network; import java.util.List; +import java.util.Map; import org.apache.cloudstack.api.command.admin.router.UpgradeRouterCmd; import org.apache.cloudstack.api.command.admin.router.UpgradeRouterTemplateCmd; +import com.cloud.deploy.DeploymentPlanner; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.OperationTimedoutException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.router.VirtualRouter; import com.cloud.user.Account; import com.cloud.utils.Pair; import com.cloud.vm.Nic; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; public interface VirtualNetworkApplianceService { /** @@ -62,6 +67,10 @@ public interface VirtualNetworkApplianceService { VirtualRouter startRouter(long id) throws ResourceUnavailableException, InsufficientCapacityException, ConcurrentOperationException; + void startRouterForHA(VirtualMachine vm, Map params, DeploymentPlanner planner) + throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException, + OperationTimedoutException; + VirtualRouter destroyRouter(long routerId, Account caller, Long callerUserId) throws ResourceUnavailableException, ConcurrentOperationException; VirtualRouter findRouter(long routerId); @@ -78,4 +87,8 @@ public interface VirtualNetworkApplianceService { Pair 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/VirtualRouterProvider.java b/api/src/main/java/com/cloud/network/VirtualRouterProvider.java index aca526b1832b..98410ca09f88 100644 --- a/api/src/main/java/com/cloud/network/VirtualRouterProvider.java +++ b/api/src/main/java/com/cloud/network/VirtualRouterProvider.java @@ -21,7 +21,7 @@ public interface VirtualRouterProvider extends InternalIdentity, Identity { public enum Type { - VirtualRouter, ElasticLoadBalancerVm, VPCVirtualRouter, InternalLbVm, NetScalerVm + VirtualRouter, ElasticLoadBalancerVm, VPCVirtualRouter, InternalLbVm, NetScalerVm, Nsx } public Type getType(); diff --git a/api/src/main/java/com/cloud/network/VpcVirtualNetworkApplianceService.java b/api/src/main/java/com/cloud/network/VpcVirtualNetworkApplianceService.java index 5c3ee3f1032a..cd04db802cac 100644 --- a/api/src/main/java/com/cloud/network/VpcVirtualNetworkApplianceService.java +++ b/api/src/main/java/com/cloud/network/VpcVirtualNetworkApplianceService.java @@ -29,7 +29,6 @@ public interface VpcVirtualNetworkApplianceService extends VirtualNetworkApplian /** * @param router * @param network - * @param isRedundant * @param params TODO * @return * @throws ConcurrentOperationException @@ -42,11 +41,30 @@ boolean addVpcRouterToGuestNetwork(VirtualRouter router, Network network, Map 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/BgpServiceProvider.java b/api/src/main/java/com/cloud/network/element/BgpServiceProvider.java new file mode 100644 index 000000000000..ee919cb1af7d --- /dev/null +++ b/api/src/main/java/com/cloud/network/element/BgpServiceProvider.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.network.element; + +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.network.vpc.Vpc; + +import org.apache.cloudstack.network.BgpPeer; + +import java.util.List; + +public interface BgpServiceProvider extends NetworkElement { + + boolean applyBgpPeers(Vpc vpc, Network network, List bgpPeers) throws ResourceUnavailableException; + +} diff --git a/api/src/main/java/com/cloud/network/element/LoadBalancingServiceProvider.java b/api/src/main/java/com/cloud/network/element/LoadBalancingServiceProvider.java index 1bb37be970d3..dc0f60f45196 100644 --- a/api/src/main/java/com/cloud/network/element/LoadBalancingServiceProvider.java +++ b/api/src/main/java/com/cloud/network/element/LoadBalancingServiceProvider.java @@ -48,4 +48,7 @@ public interface LoadBalancingServiceProvider extends NetworkElement, IpDeployin List updateHealthChecks(Network network, List lbrules); boolean handlesOnlyRulesInTransitionState(); + + default void expungeLbVmRefs(List vmIds, Long batchSize) { + } } diff --git a/api/src/main/java/com/cloud/network/element/NetworkACLServiceProvider.java b/api/src/main/java/com/cloud/network/element/NetworkACLServiceProvider.java index 8c3243c99f4b..852a650cfcde 100644 --- a/api/src/main/java/com/cloud/network/element/NetworkACLServiceProvider.java +++ b/api/src/main/java/com/cloud/network/element/NetworkACLServiceProvider.java @@ -21,6 +21,7 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.Network; import com.cloud.network.vpc.NetworkACLItem; +import com.cloud.network.vpc.Vpc; public interface NetworkACLServiceProvider extends NetworkElement { @@ -32,4 +33,6 @@ public interface NetworkACLServiceProvider extends NetworkElement { */ boolean applyNetworkACLs(Network config, List rules) throws ResourceUnavailableException; + boolean reorderAclRules(Vpc vpc, List networks, List networkACLItems); + } 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 fa67575edd35..cb0fc2fca981 100644 --- a/api/src/main/java/com/cloud/network/element/NetworkElement.java +++ b/api/src/main/java/com/cloud/network/element/NetworkElement.java @@ -23,6 +23,7 @@ import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.IpAddress; import com.cloud.network.Network; import com.cloud.network.Network.Capability; import com.cloud.network.Network.Provider; @@ -87,6 +88,14 @@ boolean prepare(Network network, NicProfile nic, VirtualMachineProfile vm, Deplo boolean release(Network network, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException; + /** + * Release IP from the network provider if reserved + * @param ipAddress + */ + default boolean releaseIp(IpAddress ipAddress) { + return true; + } + /** * The network is being shutdown. * @param network diff --git a/api/src/main/java/com/cloud/network/element/PortForwardingServiceProvider.java b/api/src/main/java/com/cloud/network/element/PortForwardingServiceProvider.java index e99bc2fd416b..8dcc8b6d0a47 100644 --- a/api/src/main/java/com/cloud/network/element/PortForwardingServiceProvider.java +++ b/api/src/main/java/com/cloud/network/element/PortForwardingServiceProvider.java @@ -17,12 +17,40 @@ package com.cloud.network.element; import java.util.List; +import java.util.Objects; import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.Network; +import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.PortForwardingRule; +import com.cloud.network.vpc.NetworkACLItem; public interface PortForwardingServiceProvider extends NetworkElement, IpDeployingRequester { + + static String getPublicPortRange(PortForwardingRule rule) { + return Objects.equals(rule.getSourcePortStart(), rule.getSourcePortEnd()) ? + String.valueOf(rule.getSourcePortStart()) : + String.valueOf(rule.getSourcePortStart()).concat("-").concat(String.valueOf(rule.getSourcePortEnd())); + } + + static String getPrivatePFPortRange(PortForwardingRule rule) { + return rule.getDestinationPortStart() == rule.getDestinationPortEnd() ? + String.valueOf(rule.getDestinationPortStart()) : + String.valueOf(rule.getDestinationPortStart()).concat("-").concat(String.valueOf(rule.getDestinationPortEnd())); + } + + static String getPrivatePortRange(FirewallRule rule) { + return Objects.equals(rule.getSourcePortStart(), rule.getSourcePortEnd()) ? + String.valueOf(rule.getSourcePortStart()) : + String.valueOf(rule.getSourcePortStart()).concat("-").concat(String.valueOf(rule.getSourcePortEnd())); + } + + static String getPrivatePortRangeForACLRule(NetworkACLItem rule) { + return Objects.equals(rule.getSourcePortStart(), rule.getSourcePortEnd()) ? + String.valueOf(rule.getSourcePortStart()) : + String.valueOf(rule.getSourcePortStart()).concat("-").concat(String.valueOf(rule.getSourcePortEnd())); + } + /** * Apply rules * @param network diff --git a/api/src/main/java/com/cloud/network/element/VpcProvider.java b/api/src/main/java/com/cloud/network/element/VpcProvider.java index 14e86195a704..fe8c8f8612f7 100644 --- a/api/src/main/java/com/cloud/network/element/VpcProvider.java +++ b/api/src/main/java/com/cloud/network/element/VpcProvider.java @@ -22,6 +22,7 @@ import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.IpAddress; import com.cloud.network.vpc.NetworkACLItem; import com.cloud.network.vpc.PrivateGateway; import com.cloud.network.vpc.StaticRouteProfile; @@ -52,4 +53,10 @@ boolean implementVpc(Vpc vpc, DeployDestination dest, ReservationContext context boolean applyStaticRoutes(Vpc vpc, List routes) throws ResourceUnavailableException; boolean applyACLItemsToPrivateGw(PrivateGateway gateway, List rules) throws ResourceUnavailableException; + + boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address); + + default boolean updateVpc(Vpc vpc, String previousVpcName) { + return true; + } } diff --git a/api/src/main/java/com/cloud/network/guru/NetworkGuru.java b/api/src/main/java/com/cloud/network/guru/NetworkGuru.java index 52f654007f28..ced664e54a96 100644 --- a/api/src/main/java/com/cloud/network/guru/NetworkGuru.java +++ b/api/src/main/java/com/cloud/network/guru/NetworkGuru.java @@ -79,20 +79,24 @@ public interface NetworkGuru extends Adapter { * be used to make determination can be isolation methods, services * provided on the guest network and the service provider that's on the * guest network. - * + *

* If a network is already fully substantiated with the necessary resources * during this design phase, then the state should be set to Setup. If * the resources are not allocated at this point, the state should be set * to Allocated. * - * @param offering network offering that contains the package of services - * the end user intends to use on that network. - * @param plan where is this network being deployed. + * @param offering network offering that contains the package of services + * the end user intends to use on that network. + * @param plan where is this network being deployed. * @param userSpecified user specified parameters for this network. - * @param owner owner of this network. + * @param name + * @param vpcId + * @param owner owner of this network. * @return Network */ - Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, Account owner); + Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, String name, Long vpcId, Account owner); + + void setup(Network network, long networkId); /** * For guest networks that are in Allocated state after the design stage, @@ -208,4 +212,11 @@ void reserve(NicProfile nic, Network network, VirtualMachineProfile vm, DeployDe boolean isMyTrafficType(TrafficType type); + default boolean isSlaacV6Only() { + return true; + } + + default boolean update(Network network, String prevNetworkName) { + return true; + } } diff --git a/api/src/main/java/com/cloud/network/lb/LoadBalancingRule.java b/api/src/main/java/com/cloud/network/lb/LoadBalancingRule.java index 64b2aeedf128..e4cf4ec526f0 100644 --- a/api/src/main/java/com/cloud/network/lb/LoadBalancingRule.java +++ b/api/src/main/java/com/cloud/network/lb/LoadBalancingRule.java @@ -63,6 +63,10 @@ public long getId() { return lb.getId(); } + public LoadBalancer getLb() { + return lb; + } + public String getName() { return lb.getName(); } 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 46f17237e029..0bf06be15d87 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, @@ -106,7 +116,7 @@ LoadBalancer createPublicLoadBalancerRule(String xId, String name, String descri boolean applyLoadBalancerConfig(long lbRuleId) throws ResourceUnavailableException; - boolean assignCertToLoadBalancer(long lbRuleId, Long certId); + boolean assignCertToLoadBalancer(long lbRuleId, Long certId, boolean isForced); boolean removeCertFromLoadBalancer(long lbRuleId); diff --git a/api/src/main/java/com/cloud/network/netris/NetrisLbBackend.java b/api/src/main/java/com/cloud/network/netris/NetrisLbBackend.java new file mode 100644 index 000000000000..afc21f7f511b --- /dev/null +++ b/api/src/main/java/com/cloud/network/netris/NetrisLbBackend.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.network.netris; + +public class NetrisLbBackend { + private long vmId; + private String vmIp; + private int port; + + public NetrisLbBackend(long vmId, String vmIp, int port) { + this.vmId = vmId; + this.vmIp = vmIp; + this.port = port; + } + + public long getVmId() { + return vmId; + } + + public String getVmIp() { + return vmIp; + } + + public int getPort() { + return port; + } +} diff --git a/api/src/main/java/com/cloud/network/netris/NetrisNetworkRule.java b/api/src/main/java/com/cloud/network/netris/NetrisNetworkRule.java new file mode 100644 index 000000000000..211517ead491 --- /dev/null +++ b/api/src/main/java/com/cloud/network/netris/NetrisNetworkRule.java @@ -0,0 +1,108 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.netris; + +import com.cloud.network.SDNProviderNetworkRule; + + +import java.util.List; + +public class NetrisNetworkRule { + public enum NetrisRuleAction { + PERMIT, DENY + } + + private SDNProviderNetworkRule baseRule; + private NetrisRuleAction aclAction; + private List lbBackends; + private String lbRuleName; + private String lbCidrList; + private String reason; + + public NetrisNetworkRule(Builder builder) { + this.baseRule = builder.baseRule; + this.aclAction = builder.aclAction; + this.lbBackends = builder.lbBackends; + this.reason = builder.reason; + this.lbCidrList = builder.lbCidrList; + this.lbRuleName = builder.lbRuleName; + } + + public NetrisRuleAction getAclAction() { + return aclAction; + } + + public List getLbBackends() { + return lbBackends; + } + + public String getReason() { + return reason; + } + + public String getLbCidrList() {return lbCidrList; } + + public String getLbRuleName() { return lbRuleName; } + + public SDNProviderNetworkRule getBaseRule() { + return baseRule; + } + + // Builder class extending the parent builder + public static class Builder { + private SDNProviderNetworkRule baseRule; + private NetrisRuleAction aclAction; + private List lbBackends; + private String reason; + private String lbCidrList; + private String lbRuleName; + + public Builder baseRule(SDNProviderNetworkRule baseRule) { + this.baseRule = baseRule; + return this; + } + + public Builder aclAction(NetrisRuleAction aclAction) { + this.aclAction = aclAction; + return this; + } + + public Builder lbBackends(List lbBackends) { + this.lbBackends = lbBackends; + return this; + } + + public Builder reason(String reason) { + this.reason = reason; + return this; + } + + public Builder lbCidrList(String lbCidrList) { + this.lbCidrList = lbCidrList; + return this; + } + + public Builder lbRuleName(String lbRuleName) { + this.lbRuleName = lbRuleName; + return this; + } + + public NetrisNetworkRule build() { + return new NetrisNetworkRule(this); + } + } +} diff --git a/api/src/main/java/com/cloud/network/netris/NetrisProvider.java b/api/src/main/java/com/cloud/network/netris/NetrisProvider.java new file mode 100644 index 000000000000..fccf2930e976 --- /dev/null +++ b/api/src/main/java/com/cloud/network/netris/NetrisProvider.java @@ -0,0 +1,30 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.netris; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +public interface NetrisProvider extends InternalIdentity, Identity { + long getZoneId(); + String getName(); + String getUrl(); + String getUsername(); + String getSiteName(); + String getTenantName(); + String getNetrisTag(); +} diff --git a/api/src/main/java/com/cloud/network/netris/NetrisService.java b/api/src/main/java/com/cloud/network/netris/NetrisService.java new file mode 100644 index 000000000000..110e9f07105a --- /dev/null +++ b/api/src/main/java/com/cloud/network/netris/NetrisService.java @@ -0,0 +1,310 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.netris; + +import com.cloud.network.IpAddress; +import com.cloud.network.Network; +import com.cloud.network.SDNProviderNetworkRule; +import com.cloud.network.vpc.StaticRoute; +import com.cloud.network.vpc.Vpc; + +import java.util.List; + +/** + * Interface for Netris Services that provides methods to manage VPCs, networks, + * NAT rules, network rules, and static routes in an SDN (Software Defined Networking) environment. + */ + +public interface NetrisService { + + /** + * Creates IPAM (IP Address Management) allocations for zone-level public ranges. + * + * @param zoneId the ID of the zone + * @return true if the operation is successful, false otherwise + */ + boolean createIPAMAllocationsForZoneLevelPublicRanges(long zoneId); + + /** + * Creates a VPC (Virtual Private Cloud) resource. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpcId the ID of the VPC + * @param vpcName the name of the VPC + * @param sourceNatEnabled true if source NAT is enabled + * @param cidr the CIDR of the VPC + * @param isVpcNetwork true if it is a VPC network + * @return true if the operation is successful, false otherwise + */ + boolean createVpcResource(long zoneId, long accountId, long domainId, Long vpcId, String vpcName, boolean sourceNatEnabled, String cidr, boolean isVpcNetwork); + + /** + * Updates an existing VPC resource. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpcId the ID of the VPC + * @param vpcName the new name of the VPC + * @param previousVpcName the previous name of the VPC + * @return true if the operation is successful, false otherwise + */ + boolean updateVpcResource(long zoneId, long accountId, long domainId, Long vpcId, String vpcName, String previousVpcName); + + /** + * Deletes a VPC resource. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpc the VPC to delete + * @return true if the operation is successful, false otherwise + */ + boolean deleteVpcResource(long zoneId, long accountId, long domainId, Vpc vpc); + + /** + * Creates a virtual network (vNet) resource. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpcName the name of the VPC + * @param vpcId the ID of the VPC + * @param networkName the name of the network + * @param networkId the ID of the network + * @param cidr the CIDR of the network + * @param globalRouting true if global routing is enabled + * @return true if the operation is successful, false otherwise + */ + boolean createVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr, Boolean globalRouting); + + /** + * Updates an existing vNet resource. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpcName the name of the VPC + * @param vpcId the ID of the VPC + * @param networkName the new name of the network + * @param networkId the ID of the network + * @param prevNetworkName the previous name of the network + * @return true if the operation is successful, false otherwise + */ + boolean updateVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String prevNetworkName); + + /** + * Deletes an existing vNet resource. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpcName the name of the VPC + * @param vpcId the ID of the VPC + * @param networkName the name of the network + * @param networkId the ID of the network + * @param cidr the CIDR of the network + * @return true if the operation is successful, false otherwise + */ + boolean deleteVnetResource(long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr); + + /** + * Creates a source NAT rule for a VPC or network. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpcName the name of the VPC + * @param vpcId the ID of the VPC + * @param networkName the name of the network + * @param networkId the ID of the network + * @param isForVpc true if the rule applies to a VPC + * @param vpcCidr the VPC CIDR + * @param sourceNatIp the source NAT IP + * @return true if the operation is successful, false otherwise + */ + boolean createSnatRule(long zoneId, long accountId, long domainId, String vpcName, long vpcId, String networkName, long networkId, boolean isForVpc, String vpcCidr, String sourceNatIp); + + /** + * Creates a port forwarding rule for a VPC or network. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpcName the name of the VPC + * @param vpcId the ID of the VPC + * @param networkName the name of the network + * @param networkId the ID of the network + * @param isForVpc true if the rule applies to a VPC + * @param vpcCidr the VPC CIDR + * @param networkRule the network rule to forward + * @return true if the operation is successful, false otherwise + */ + boolean createPortForwardingRule(long zoneId, long accountId, long domainId, String vpcName, long vpcId, String networkName, Long networkId, boolean isForVpc, String vpcCidr, SDNProviderNetworkRule networkRule); + + /** + * Deletes a port forwarding rule for a VPC or network. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpcName the name of the VPC + * @param vpcId the ID of the VPC + * @param networkName the name of the network + * @param networkId the ID of the network + * @param isForVpc true if the rule applies to a VPC + * @param vpcCidr the VPC CIDR + * @param networkRule the network rule to remove + * @return true if the operation is successful, false otherwise + */ + boolean deletePortForwardingRule(long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, boolean isForVpc, String vpcCidr, SDNProviderNetworkRule networkRule); + + /** + * Updates the source NAT IP for a specified VPC. + * + * @param vpc the VPC to updates + * @param address the new source NAT IP address + * @return true if the operation is successful, false otherwise + */ + boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address); + + /** + * Creates a static NAT rule for a specific VM. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param networkResourceName the name of the network resource + * @param networkResourceId the ID of the network resource + * @param isForVpc true if the rule applies to a VPC + * @param vpcCidr the VPC CIDR + * @param staticNatIp the static NAT IP + * @param vmIp the VM's IP address + * @param vmId the ID of the VM + * @return true if the operation is successful, false otherwise + */ + boolean createStaticNatRule(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String vpcCidr, String staticNatIp, String vmIp, long vmId); + + /** + * Deletes a static NAT rule for a specific VM. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param networkResourceName the name of the network resource + * @param networkResourceId the ID of the network resource + * @param isForVpc true if the rule applies to a VPC + * @param staticNatIp the static NAT IP + * @param vmId the ID of the VM + * @return true if the operation is successful, false otherwise + */ + boolean deleteStaticNatRule(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String staticNatIp, long vmId); + + /** + * Adds firewall rules to a specific network. + * + * @param network the target network + * @param firewallRules the list of firewall rules to add + * @return true if the operation is successful, false otherwise + */ + boolean addFirewallRules(Network network, List firewallRules); + + /** + * Deletes firewall rules from a specific network. + * + * @param network the target network + * @param firewallRules the list of firewall rules to delete + * @return true if the operation is successful, false otherwise + */ + boolean deleteFirewallRules(Network network, List firewallRules); + + /** + * Adds or updates a static route for a specific network or VPC. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param networkResourceName the name of the network resource + * @param networkResourceId the ID of the network resource + * @param isForVpc true if it is for a VPC + * @param prefix the IP prefix of the route + * @param nextHop the next hop address + * @param routeId the ID of the route + * @param updateRoute true if the route should be updated + * @return true if the operation is successful, false otherwise + */ + boolean addOrUpdateStaticRoute(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String prefix, String nextHop, Long routeId, boolean updateRoute); + + /** + * Deletes a specific static route for a network or VPC. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param networkResourceName the name of the network resource + * @param networkResourceId the ID of the network resource + * @param isForVpc true if it is for a VPC + * @param prefix the IP prefix of the route + * @param nextHop the next hop address + * @param routeId the ID of the route + * @return true if the operation is successful, false otherwise + */ + boolean deleteStaticRoute(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String prefix, String nextHop, Long routeId); + + /** + * Lists static routes for a specific network or VPC. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param networkResourceName the name of the network resource + * @param networkResourceId the ID of the network resource + * @param isForVpc true if it is for a VPC + * @param prefix the IP prefix of the route + * @param nextHop the next hop address + * @param routeId the ID of the route + * @return a list of static routes + */ + List listStaticRoutes(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String prefix, String nextHop, Long routeId); + + /** + * Releases a NAT IP address. + * + * @param zoneId the ID of the zone + * @param publicIp the public NAT IP to release + * @return true if the operation is successful, false otherwise + */ + boolean releaseNatIp(long zoneId, String publicIp); + + /** + * Creates or updates a load balancer (LB) rule. + * + * @param rule the network rule for the load balancer + * @return true if the operation is successful, false otherwise + */ + boolean createOrUpdateLbRule(NetrisNetworkRule rule); + + /** + * Deletes a load balancer (LB) rule. + * + * @param rule the network rule to delete + * @return true if the operation is successful, false otherwise + */ + boolean deleteLbRule(NetrisNetworkRule rule); +} diff --git a/api/src/main/java/com/cloud/network/nsx/NsxProvider.java b/api/src/main/java/com/cloud/network/nsx/NsxProvider.java new file mode 100644 index 000000000000..19cb3b4b939e --- /dev/null +++ b/api/src/main/java/com/cloud/network/nsx/NsxProvider.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.network.nsx; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +public interface NsxProvider extends InternalIdentity, Identity { + String getHostname(); + + String getPort(); + String getProviderName(); + String getUsername(); + long getZoneId(); + + String getTier0Gateway(); + String getEdgeCluster(); + + String getTransportZone(); +} diff --git a/api/src/main/java/com/cloud/network/nsx/NsxService.java b/api/src/main/java/com/cloud/network/nsx/NsxService.java new file mode 100644 index 000000000000..1adb7461cc09 --- /dev/null +++ b/api/src/main/java/com/cloud/network/nsx/NsxService.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.network.nsx; + +import org.apache.cloudstack.framework.config.ConfigKey; + +import com.cloud.network.IpAddress; +import com.cloud.network.vpc.Vpc; + +public interface NsxService { + + ConfigKey NSX_API_FAILURE_RETRIES = new ConfigKey<>("Advanced", Integer.class, + "nsx.api.failure.retries", "30", + "Number of retries for NSX API operations in case of failures", + true, ConfigKey.Scope.Zone); + ConfigKey NSX_API_FAILURE_INTERVAL = new ConfigKey<>("Advanced", Integer.class, + "nsx.api.failure.interval", "60", + "Waiting time (in seconds) before retrying an NSX API operation in case of failure", + true, ConfigKey.Scope.Zone); + + boolean createVpcNetwork(Long zoneId, long accountId, long domainId, Long vpcId, String vpcName, boolean sourceNatEnabled); + boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address); + String getSegmentId(long domainId, long accountId, long zoneId, Long vpcId, long networkId); +} diff --git a/api/src/main/java/com/cloud/network/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/rules/RulesService.java b/api/src/main/java/com/cloud/network/rules/RulesService.java index 0b4afeef9458..547d4ab51e03 100644 --- a/api/src/main/java/com/cloud/network/rules/RulesService.java +++ b/api/src/main/java/com/cloud/network/rules/RulesService.java @@ -26,6 +26,7 @@ import com.cloud.user.Account; import com.cloud.utils.Pair; import com.cloud.utils.net.Ip; +import org.apache.cloudstack.api.command.user.firewall.UpdatePortForwardingRuleCmd; public interface RulesService { Pair, Integer> searchStaticNatRules(Long ipId, Long id, Long vmId, Long start, Long size, String accountName, Long domainId, @@ -81,6 +82,8 @@ Pair, Integer> searchStaticNatRules(Long ipId, Long boolean disableStaticNat(long ipId) throws ResourceUnavailableException, NetworkRuleConflictException, InsufficientAddressCapacityException; - PortForwardingRule updatePortForwardingRule(long id, Integer privatePort, Integer privateEndPort, Long virtualMachineId, Ip vmGuestIp, String customId, Boolean forDisplay); + PortForwardingRule updatePortForwardingRule(UpdatePortForwardingRuleCmd cmd); + + void validatePortForwardingSourceCidrList(List sourceCidrList); } 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/StaticRoute.java b/api/src/main/java/com/cloud/network/vpc/StaticRoute.java index 5707ca140246..739fca328b8c 100644 --- a/api/src/main/java/com/cloud/network/vpc/StaticRoute.java +++ b/api/src/main/java/com/cloud/network/vpc/StaticRoute.java @@ -25,6 +25,7 @@ enum State { Staged, // route been created but has never got through network rule conflict detection. Routes in this state can not be sent to VPC virtual router. Add, // Add means the route has been created and has gone through network rule conflict detection. Active, // Route has been sent to the VPC router and reported to be active. + Update, Revoke, // Revoke means this route has been revoked. If this route has been sent to the VPC router, the route will be deleted from database. Deleting // rule has been revoked and is scheduled for deletion } @@ -32,7 +33,9 @@ enum State { /** * @return */ - long getVpcGatewayId(); + Long getVpcGatewayId(); + + String getNextHop(); /** * @return diff --git a/api/src/main/java/com/cloud/network/vpc/StaticRouteProfile.java b/api/src/main/java/com/cloud/network/vpc/StaticRouteProfile.java index cb4849f1f7b2..c8fc073911fe 100644 --- a/api/src/main/java/com/cloud/network/vpc/StaticRouteProfile.java +++ b/api/src/main/java/com/cloud/network/vpc/StaticRouteProfile.java @@ -23,7 +23,8 @@ public class StaticRouteProfile implements StaticRoute { private String targetCidr; private long accountId; private long domainId; - private long gatewayId; + private Long gatewayId; + private String nextHop; private StaticRoute.State state; private long vpcId; String vlanTag; @@ -46,6 +47,18 @@ public StaticRouteProfile(StaticRoute staticRoute, VpcGateway gateway) { ipAddress = gateway.getIp4Address(); } + public StaticRouteProfile(StaticRoute staticRoute) { + id = staticRoute.getId(); + uuid = staticRoute.getUuid(); + targetCidr = staticRoute.getCidr(); + accountId = staticRoute.getAccountId(); + domainId = staticRoute.getDomainId(); + gatewayId = staticRoute.getVpcGatewayId(); + state = staticRoute.getState(); + vpcId = staticRoute.getVpcId(); + gateway = staticRoute.getNextHop(); + } + @Override public long getAccountId() { return accountId; @@ -57,10 +70,15 @@ public long getDomainId() { } @Override - public long getVpcGatewayId() { + public Long getVpcGatewayId() { return gatewayId; } + @Override + public String getNextHop() { + return nextHop; + } + @Override public String getCidr() { return targetCidr; diff --git a/api/src/main/java/com/cloud/network/vpc/Vpc.java b/api/src/main/java/com/cloud/network/vpc/Vpc.java index e9a831c9d839..b94089d2d433 100644 --- a/api/src/main/java/com/cloud/network/vpc/Vpc.java +++ b/api/src/main/java/com/cloud/network/vpc/Vpc.java @@ -105,4 +105,6 @@ public enum State { String getIp6Dns1(); String getIp6Dns2(); + + boolean useRouterIpAsResolver(); } diff --git a/api/src/main/java/com/cloud/network/vpc/VpcOffering.java b/api/src/main/java/com/cloud/network/vpc/VpcOffering.java index b4df8e38dbac..17f49bb36521 100644 --- a/api/src/main/java/com/cloud/network/vpc/VpcOffering.java +++ b/api/src/main/java/com/cloud/network/vpc/VpcOffering.java @@ -18,6 +18,7 @@ import java.util.Date; +import com.cloud.offering.NetworkOffering; import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; @@ -29,6 +30,10 @@ public enum State { public static final String defaultVPCOfferingName = "Default VPC offering"; public static final String defaultVPCNSOfferingName = "Default VPC offering with Netscaler"; public static final String redundantVPCOfferingName = "Redundant VPC offering"; + public static final String DEFAULT_VPC_NAT_NSX_OFFERING_NAME = "VPC offering with NSX - NAT Mode"; + public static final String DEFAULT_VPC_ROUTE_NSX_OFFERING_NAME = "VPC offering with NSX - Route Mode"; + public static final String DEFAULT_VPC_ROUTE_NETRIS_OFFERING_NAME = "VPC offering with Netris - Route Mode"; + public static final String DEFAULT_VPC_NAT_NETRIS_OFFERING_NAME = "VPC offering with Netris - NAT Mode"; /** * @@ -53,6 +58,8 @@ public enum State { */ boolean isDefault(); + NetworkOffering.NetworkMode getNetworkMode(); + /** * @return service offering id used by VPC virtual router */ @@ -73,4 +80,8 @@ public enum State { Date getRemoved(); Date getCreated(); + + NetworkOffering.RoutingMode getRoutingMode(); + + Boolean isSpecifyAsNumber(); } 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 5cccd6c5a823..97b95339ecf3 100644 --- a/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java +++ b/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java @@ -24,6 +24,7 @@ import org.apache.cloudstack.api.command.admin.vpc.UpdateVPCOfferingCmd; import org.apache.cloudstack.api.command.user.vpc.ListVPCOfferingsCmd; +import com.cloud.offering.NetworkOffering; import com.cloud.utils.Pair; import com.cloud.utils.net.NetUtils; @@ -36,7 +37,10 @@ public interface VpcProvisioningService { VpcOffering createVpcOffering(String name, String displayText, List supportedServices, Map> serviceProviders, Map serviceCapabilitystList, NetUtils.InternetProtocol internetProtocol, - Long serviceOfferingId, List domainIds, List zoneIds, VpcOffering.State state); + Long serviceOfferingId, String externalProvider, NetworkOffering.NetworkMode networkMode, + List domainIds, List zoneIds, VpcOffering.State state, + NetworkOffering.RoutingMode routingMode, boolean specifyAsNumber); + 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 2cdc034a16e1..c1546609d2b7 100644 --- a/api/src/main/java/com/cloud/network/vpc/VpcService.java +++ b/api/src/main/java/com/cloud/network/vpc/VpcService.java @@ -48,16 +48,17 @@ public interface VpcService { * @param vpcName * @param displayText * @param cidr - * @param networkDomain TODO + * @param networkDomain TODO * @param ip4Dns1 * @param ip4Dns2 - * @param displayVpc TODO + * @param displayVpc TODO + * @param useVrIpResolver * @return * @throws ResourceAllocationException TODO */ Vpc createVpc(long zoneId, long vpcOffId, long vpcOwnerId, String vpcName, String displayText, String cidr, String networkDomain, - String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2, Boolean displayVpc, Integer publicMtu) - throws ResourceAllocationException; + String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2, Boolean displayVpc, Integer publicMtu, Integer cidrSize, + Long asNumber, List bgpPeerIds, Boolean useVrIpResolver) throws ResourceAllocationException; /** * Persists VPC record in the database @@ -132,6 +133,8 @@ Pair, Integer> listVpcs(Long id, String vpcName, String disp */ boolean startVpc(long vpcId, boolean destroyOnFailure) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException; + void startVpc(CreateVPCCmd cmd) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException; + /** * Shuts down the VPC which includes shutting down all VPC provider and rules cleanup on the backend * @@ -235,7 +238,7 @@ Pair, Integer> listVpcs(Long id, String vpcName, String disp * @param cidr * @return */ - StaticRoute createStaticRoute(long gatewayId, String cidr) throws NetworkRuleConflictException; + StaticRoute createStaticRoute(Long gatewayId, Long vpcId, String nextHop, String cidr) throws NetworkRuleConflictException; /** * Lists static routes based on parameters passed to the call diff --git a/api/src/main/java/com/cloud/network/vpn/RemoteAccessVpnService.java b/api/src/main/java/com/cloud/network/vpn/RemoteAccessVpnService.java index bbb9771d27aa..ffa8af4576d7 100644 --- a/api/src/main/java/com/cloud/network/vpn/RemoteAccessVpnService.java +++ b/api/src/main/java/com/cloud/network/vpn/RemoteAccessVpnService.java @@ -39,7 +39,7 @@ public interface RemoteAccessVpnService { VpnUser addVpnUser(long vpnOwnerId, String userName, String password); - boolean removeVpnUser(long vpnOwnerId, String userName, Account caller); + boolean removeVpnUser(Account vpnOwner, String userName, Account caller); List listVpnUsers(long vpnOwnerId, String userName); 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 d83039e15c2b..12dcf423e34f 100644 --- a/api/src/main/java/com/cloud/offering/DiskOfferingInfo.java +++ b/api/src/main/java/com/cloud/offering/DiskOfferingInfo.java @@ -31,6 +31,13 @@ public DiskOfferingInfo(DiskOffering diskOffering) { _diskOffering = diskOffering; } + public DiskOfferingInfo(DiskOffering diskOffering, Long size, Long minIops, Long maxIops) { + _diskOffering = diskOffering; + _size = size; + _minIops = minIops; + _maxIops = maxIops; + } + public void setDiskOffering(DiskOffering diskOffering) { _diskOffering = diskOffering; } diff --git a/api/src/main/java/com/cloud/offering/NetworkOffering.java b/api/src/main/java/com/cloud/offering/NetworkOffering.java index 207880ea28c0..5000a4f8c626 100644 --- a/api/src/main/java/com/cloud/offering/NetworkOffering.java +++ b/api/src/main/java/com/cloud/offering/NetworkOffering.java @@ -43,6 +43,15 @@ public enum Detail { InternalLbProvider, PublicLbProvider, servicepackageuuid, servicepackagedescription, PromiscuousMode, MacAddressChanges, ForgedTransmits, MacLearning, RelatedNetworkOffering, domainid, zoneid, pvlanType, internetProtocol } + public enum NetworkMode { + NATTED, + ROUTED + } + + enum RoutingMode { + Static, Dynamic + } + public final static String SystemPublicNetwork = "System-Public-Network"; public final static String SystemControlNetwork = "System-Control-Network"; public final static String SystemManagementNetwork = "System-Management-Network"; @@ -52,6 +61,13 @@ public enum Detail { public final static String DefaultSharedNetworkOfferingWithSGService = "DefaultSharedNetworkOfferingWithSGService"; public static final String DEFAULT_TUNGSTEN_SHARED_NETWORK_OFFERING_WITH_SGSERVICE = "DefaultTungstenSharedNetworkOfferingWithSGService"; + public static final String DEFAULT_NAT_NSX_OFFERING_FOR_VPC = "DefaultNATNSXNetworkOfferingForVpc"; + public static final String DEFAULT_NAT_NSX_OFFERING_FOR_VPC_WITH_ILB = "DefaultNATNSXNetworkOfferingForVpcWithInternalLB"; + public static final String DEFAULT_ROUTED_NSX_OFFERING_FOR_VPC = "DefaultRoutedNSXNetworkOfferingForVpc"; + public static final String DEFAULT_ROUTED_NETRIS_OFFERING_FOR_VPC = "DefaultRoutedNetrisNetworkOfferingForVpc"; + public static final String DEFAULT_NAT_NETRIS_OFFERING_FOR_VPC = "DefaultNATNetrisNetworkOfferingForVpc"; + public static final String DEFAULT_NAT_NSX_OFFERING = "DefaultNATNSXNetworkOffering"; + public static final String DEFAULT_ROUTED_NSX_OFFERING = "DefaultRoutedNSXNetworkOffering"; public final static String QuickCloudNoServices = "QuickCloudNoServices"; public final static String DefaultIsolatedNetworkOfferingWithSourceNatService = "DefaultIsolatedNetworkOfferingWithSourceNatService"; public final static String OvsIsolatedNetworkOfferingWithSourceNatService = "OvsIsolatedNetworkOfferingWithSourceNatService"; @@ -88,7 +104,7 @@ public enum Detail { boolean isForVpc(); - boolean isForTungsten(); + NetworkMode getNetworkMode(); TrafficType getTrafficType(); @@ -151,4 +167,8 @@ public enum Detail { String getServicePackage(); Date getCreated(); + + RoutingMode getRoutingMode(); + + Boolean isSpecifyAsNumber(); } diff --git a/api/src/main/java/com/cloud/offering/ServiceOffering.java b/api/src/main/java/com/cloud/offering/ServiceOffering.java index 278acbe231ef..532123e4373a 100644 --- a/api/src/main/java/com/cloud/offering/ServiceOffering.java +++ b/api/src/main/java/com/cloud/offering/ServiceOffering.java @@ -33,6 +33,9 @@ public interface ServiceOffering extends InfrastructureEntity, InternalIdentity, static final String internalLbVmDefaultOffUniqueName = "Cloud.Com-InternalLBVm"; // leaving cloud.com references as these are identifyers and no real world addresses (check against DB) + + static final String PURGE_DB_ENTITIES_KEY = "purge.db.entities"; + enum State { Inactive, Active, } @@ -102,7 +105,7 @@ enum StorageType { boolean getDefaultUse(); - String getSystemVmType(); + String getVmType(); String getDeploymentPlanner(); @@ -139,4 +142,8 @@ enum StorageType { Boolean getDiskOfferingStrictness(); void setDiskOfferingStrictness(boolean diskOfferingStrictness); + + Long getVgpuProfileId(); + + Integer getGpuCount(); } diff --git a/api/src/main/java/com/cloud/org/Cluster.java b/api/src/main/java/com/cloud/org/Cluster.java index 4079c88dfde3..b0aa6bb04cf2 100644 --- a/api/src/main/java/com/cloud/org/Cluster.java +++ b/api/src/main/java/com/cloud/org/Cluster.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.org; +import com.cloud.cpu.CPU; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.org.Managed.ManagedState; import org.apache.cloudstack.kernel.Partition; @@ -38,4 +39,8 @@ public static enum ClusterType { AllocationState getAllocationState(); ManagedState getManagedState(); + + CPU.CPUArch getArch(); + + String getStorageAccessGroups(); } diff --git a/api/src/main/java/com/cloud/region/ha/GlobalLoadBalancingRulesService.java b/api/src/main/java/com/cloud/region/ha/GlobalLoadBalancingRulesService.java index ab6e6fb6c5ae..3b61367e3b4d 100644 --- a/api/src/main/java/com/cloud/region/ha/GlobalLoadBalancingRulesService.java +++ b/api/src/main/java/com/cloud/region/ha/GlobalLoadBalancingRulesService.java @@ -19,6 +19,7 @@ import java.util.List; +import com.cloud.user.Account; import org.apache.cloudstack.api.command.user.region.ha.gslb.AssignToGlobalLoadBalancerRuleCmd; import org.apache.cloudstack.api.command.user.region.ha.gslb.CreateGlobalLoadBalancerRuleCmd; import org.apache.cloudstack.api.command.user.region.ha.gslb.DeleteGlobalLoadBalancerRuleCmd; @@ -39,7 +40,7 @@ public interface GlobalLoadBalancingRulesService { GlobalLoadBalancerRule updateGlobalLoadBalancerRule(UpdateGlobalLoadBalancerRuleCmd updateGslbCmd); - boolean revokeAllGslbRulesForAccount(com.cloud.user.Account caller, long accountId) throws com.cloud.exception.ResourceUnavailableException; + boolean revokeAllGslbRulesForAccount(com.cloud.user.Account caller, Account account) throws com.cloud.exception.ResourceUnavailableException; /* * methods for managing sites participating in global load balancing diff --git a/api/src/main/java/com/cloud/resource/ResourceService.java b/api/src/main/java/com/cloud/resource/ResourceService.java index 2757c918ed65..3cdf8fc64e99 100644 --- a/api/src/main/java/com/cloud/resource/ResourceService.java +++ b/api/src/main/java/com/cloud/resource/ResourceService.java @@ -23,11 +23,11 @@ import org.apache.cloudstack.api.command.admin.cluster.UpdateClusterCmd; import org.apache.cloudstack.api.command.admin.host.AddHostCmd; import org.apache.cloudstack.api.command.admin.host.AddSecondaryStorageCmd; -import org.apache.cloudstack.api.command.admin.host.CancelMaintenanceCmd; +import org.apache.cloudstack.api.command.admin.host.CancelHostMaintenanceCmd; import org.apache.cloudstack.api.command.admin.host.ReconnectHostCmd; import org.apache.cloudstack.api.command.admin.host.UpdateHostCmd; import org.apache.cloudstack.api.command.admin.host.UpdateHostPasswordCmd; -import org.apache.cloudstack.api.command.admin.host.PrepareForMaintenanceCmd; +import org.apache.cloudstack.api.command.admin.host.PrepareForHostMaintenanceCmd; import org.apache.cloudstack.api.command.admin.host.DeclareHostAsDegradedCmd; import org.apache.cloudstack.api.command.admin.host.CancelHostAsDegradedCmd; @@ -51,7 +51,7 @@ public interface ResourceService { Host autoUpdateHostAllocationState(Long hostId, ResourceState.Event resourceEvent) throws NoTransitionException; - Host cancelMaintenance(CancelMaintenanceCmd cmd); + Host cancelMaintenance(CancelHostMaintenanceCmd cmd); Host reconnectHost(ReconnectHostCmd cmd) throws AgentUnavailableException; @@ -69,7 +69,7 @@ public interface ResourceService { List discoverHosts(AddSecondaryStorageCmd cmd) throws IllegalArgumentException, DiscoveryException, InvalidParameterValueException; - Host maintain(PrepareForMaintenanceCmd cmd); + Host maintain(PrepareForHostMaintenanceCmd cmd); Host declareHostAsDegraded(DeclareHostAsDegradedCmd cmd) throws NoTransitionException; @@ -95,4 +95,11 @@ public interface ResourceService { boolean releaseHostReservation(Long hostId); + void updatePodStorageAccessGroups(long podId, List newStorageAccessGroups); + + void updateZoneStorageAccessGroups(long zoneId, List newStorageAccessGroups); + + void updateClusterStorageAccessGroups(Long clusterId, List newStorageAccessGroups); + + void updateHostStorageAccessGroups(Long hostId, List newStorageAccessGroups); } diff --git a/api/src/main/java/com/cloud/resource/ResourceState.java b/api/src/main/java/com/cloud/resource/ResourceState.java index 70738c7921bc..e91cf820b081 100644 --- a/api/src/main/java/com/cloud/resource/ResourceState.java +++ b/api/src/main/java/com/cloud/resource/ResourceState.java @@ -76,6 +76,10 @@ public static Event toEvent(String e) { } } + public static List s_maintenanceStates = List.of(ResourceState.Maintenance, + ResourceState.ErrorInMaintenance, ResourceState.PrepareForMaintenance, + ResourceState.ErrorInPrepareForMaintenance); + public ResourceState getNextState(Event a) { return s_fsm.getNextState(this, a); } @@ -98,8 +102,7 @@ public static String[] toString(ResourceState... states) { } public static boolean isMaintenanceState(ResourceState state) { - return Arrays.asList(ResourceState.Maintenance, ResourceState.ErrorInMaintenance, - ResourceState.PrepareForMaintenance, ResourceState.ErrorInPrepareForMaintenance).contains(state); + return s_maintenanceStates.contains(state); } public static boolean canAttemptMaintenance(ResourceState state) { diff --git a/api/src/main/java/com/cloud/server/ManagementServerHostStats.java b/api/src/main/java/com/cloud/server/ManagementServerHostStats.java index 1f201d7689f7..6eb275031e80 100644 --- a/api/src/main/java/com/cloud/server/ManagementServerHostStats.java +++ b/api/src/main/java/com/cloud/server/ManagementServerHostStats.java @@ -19,6 +19,7 @@ package com.cloud.server; import java.util.Date; +import java.util.List; /** * management server related stats @@ -32,6 +33,8 @@ public interface ManagementServerHostStats { String getManagementServerHostUuid(); + long getManagementServerRunId(); + long getSessions(); double getCpuUtilization(); @@ -68,6 +71,10 @@ public interface ManagementServerHostStats { String getOsDistribution(); + List getLastAgents(); + + List getAgents(); + int getAgentCount(); long getHeapMemoryUsed(); diff --git a/api/src/main/java/com/cloud/server/ManagementService.java b/api/src/main/java/com/cloud/server/ManagementService.java index e87f6b362da2..51737546ffad 100644 --- a/api/src/main/java/com/cloud/server/ManagementService.java +++ b/api/src/main/java/com/cloud/server/ManagementService.java @@ -20,21 +20,24 @@ 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; import org.apache.cloudstack.api.command.admin.config.UpdateHypervisorCapabilitiesCmd; +import org.apache.cloudstack.api.command.admin.guest.AddGuestOsCategoryCmd; import org.apache.cloudstack.api.command.admin.guest.AddGuestOsCmd; import org.apache.cloudstack.api.command.admin.guest.AddGuestOsMappingCmd; +import org.apache.cloudstack.api.command.admin.guest.DeleteGuestOsCategoryCmd; import org.apache.cloudstack.api.command.admin.guest.GetHypervisorGuestOsNamesCmd; import org.apache.cloudstack.api.command.admin.guest.ListGuestOsMappingCmd; import org.apache.cloudstack.api.command.admin.guest.RemoveGuestOsCmd; import org.apache.cloudstack.api.command.admin.guest.RemoveGuestOsMappingCmd; +import org.apache.cloudstack.api.command.admin.guest.UpdateGuestOsCategoryCmd; import org.apache.cloudstack.api.command.admin.guest.UpdateGuestOsCmd; import org.apache.cloudstack.api.command.admin.guest.UpdateGuestOsMappingCmd; import org.apache.cloudstack.api.command.admin.host.ListHostsCmd; import org.apache.cloudstack.api.command.admin.host.UpdateHostPasswordCmd; +import org.apache.cloudstack.api.command.admin.management.RemoveManagementServerCmd; import org.apache.cloudstack.api.command.admin.pod.ListPodsByCmd; import org.apache.cloudstack.api.command.admin.resource.ArchiveAlertsCmd; import org.apache.cloudstack.api.command.admin.resource.DeleteAlertsCmd; @@ -59,18 +62,23 @@ import org.apache.cloudstack.api.command.user.ssh.DeleteSSHKeyPairCmd; import org.apache.cloudstack.api.command.user.ssh.ListSSHKeyPairsCmd; import org.apache.cloudstack.api.command.user.ssh.RegisterSSHKeyPairCmd; +import org.apache.cloudstack.api.command.user.userdata.DeleteCniConfigurationCmd; import org.apache.cloudstack.api.command.user.userdata.DeleteUserDataCmd; import org.apache.cloudstack.api.command.user.userdata.ListUserDataCmd; +import org.apache.cloudstack.api.command.user.userdata.RegisterCniConfigurationCmd; import org.apache.cloudstack.api.command.user.userdata.RegisterUserDataCmd; import org.apache.cloudstack.api.command.user.vm.GetVMPasswordCmd; import org.apache.cloudstack.api.command.user.vmgroup.UpdateVMGroupCmd; import org.apache.cloudstack.config.Configuration; import org.apache.cloudstack.config.ConfigurationGroup; +import org.apache.cloudstack.framework.config.ConfigKey; import com.cloud.alert.Alert; 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; @@ -85,11 +93,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. @@ -98,6 +108,14 @@ public interface ManagementService { static final String Name = "management-server"; + ConfigKey JsInterpretationEnabled = new ConfigKey<>("Hidden" + , Boolean.class + , "js.interpretation.enabled" + , "false" + , "Enable/Disable all JavaScript interpretation related functionalities to create or update Javascript rules." + , false + , ConfigKey.Scope.Global); + /** * returns the a map of the names/values in the configuration table * @@ -168,6 +186,12 @@ public interface ManagementService { */ Pair, Integer> listGuestOSCategoriesByCriteria(ListGuestOsCategoriesCmd cmd); + GuestOsCategory addGuestOsCategory(AddGuestOsCategoryCmd cmd); + + GuestOsCategory updateGuestOsCategory(UpdateGuestOsCategoryCmd cmd); + + boolean deleteGuestOsCategory(DeleteGuestOsCategoryCmd cmd); + /** * Obtains a list of all guest OS mappings * @@ -360,17 +384,23 @@ public interface ManagementService { * The api command class. * @return The list of userdatas found. */ - Pair, Integer> listUserDatas(ListUserDataCmd cmd); + Pair, Integer> listUserDatas(ListUserDataCmd cmd, boolean forCks); + + /** + * Registers a cni configuration. + * + * @param cmd The api command class. + * @return A VO with the registered user data. + */ + UserData registerCniConfiguration(RegisterCniConfigurationCmd cmd); /** * Registers a userdata. * - * @param cmd - * The api command class. + * @param cmd The api command class. * @return A VO with the registered userdata. */ UserData registerUserData(RegisterUserDataCmd cmd); - /** * Deletes a userdata. * @@ -380,6 +410,14 @@ public interface ManagementService { */ boolean deleteUserData(DeleteUserDataCmd cmd); + /** + * Deletes user data. + * + * @param cmd + * The api command class. + * @return True on success. False otherwise. + */ + boolean deleteCniConfiguration(DeleteCniConfigurationCmd cmd); /** * Search registered key pairs for the logged in user. * @@ -443,16 +481,30 @@ 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, * this method removes the child storage pools and adds the corresponding parent datastore cluster for API response listing * * @param Long volumeId + * @param String keyword if passed, will only return storage pools that contain this keyword in the name * @return Pair, List> List of storage pools in cluster and list * of pools with enough capacity. */ - Pair, List> listStoragePoolsForMigrationOfVolume(Long volumeId); + Pair, List> listStoragePoolsForMigrationOfVolume(Long volumeId, String keyword); Pair, List> listStoragePoolsForSystemMigrationOfVolume(Long volumeId, Long newDiskOfferingId, Long newSize, Long newMinIops, Long newMaxIops, boolean keepSourceStoragePool, boolean bypassStorageTypeCheck); @@ -480,4 +532,8 @@ VirtualMachine upgradeSystemVM(ScaleSystemVMCmd cmd) throws ResourceUnavailableE Pair patchSystemVM(PatchSystemVMCmd cmd); + boolean removeManagementServer(RemoveManagementServerCmd cmd); + + void checkJsInterpretationAllowedIfNeededForParameterValue(String paramName, boolean paramValue); + } diff --git a/api/src/main/java/com/cloud/server/ResourceIconManager.java b/api/src/main/java/com/cloud/server/ResourceIconManager.java index e5111d9160b8..d10b3eb0cd5b 100644 --- a/api/src/main/java/com/cloud/server/ResourceIconManager.java +++ b/api/src/main/java/com/cloud/server/ResourceIconManager.java @@ -16,7 +16,9 @@ // under the License. package com.cloud.server; +import java.util.Collection; import java.util.List; +import java.util.Map; public interface ResourceIconManager { @@ -25,4 +27,8 @@ public interface ResourceIconManager { boolean deleteResourceIcon(List resourceIds, ResourceTag.ResourceObjectType resourceType); ResourceIcon getByResourceTypeAndUuid(ResourceTag.ResourceObjectType type, String resourceId); + + Map getByResourceTypeAndIds(ResourceTag.ResourceObjectType type, Collection resourceIds); + + Map getByResourceTypeAndUuids(ResourceTag.ResourceObjectType type, Collection resourceUuids); } diff --git a/api/src/main/java/com/cloud/server/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 9bbb5d43eaeb..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 @@ -66,10 +66,11 @@ public enum ResourceObjectType { LBStickinessPolicy(false, true), LBHealthCheckPolicy(false, true), SnapshotPolicy(true, true), + GuestOsCategory(false, false, true), 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/GuestOsCategory.java b/api/src/main/java/com/cloud/storage/GuestOsCategory.java index b46418d5c8f9..e1ee44891582 100644 --- a/api/src/main/java/com/cloud/storage/GuestOsCategory.java +++ b/api/src/main/java/com/cloud/storage/GuestOsCategory.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.storage; +import java.util.Date; + import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; @@ -27,4 +29,7 @@ public interface GuestOsCategory extends Identity, InternalIdentity { void setName(String name); + boolean isFeatured(); + + Date getCreated(); } diff --git a/api/src/main/java/com/cloud/storage/MigrationOptions.java b/api/src/main/java/com/cloud/storage/MigrationOptions.java index a39a2a7c8272..8b642d09e29e 100644 --- a/api/src/main/java/com/cloud/storage/MigrationOptions.java +++ b/api/src/main/java/com/cloud/storage/MigrationOptions.java @@ -24,6 +24,7 @@ public class MigrationOptions implements Serializable { private String srcPoolUuid; private Storage.StoragePoolType srcPoolType; + private Long srcPoolClusterId; private Type type; private ScopeType scopeType; private String srcBackingFilePath; @@ -38,21 +39,23 @@ public enum Type { public MigrationOptions() { } - public MigrationOptions(String srcPoolUuid, Storage.StoragePoolType srcPoolType, String srcBackingFilePath, boolean copySrcTemplate, ScopeType scopeType) { + public MigrationOptions(String srcPoolUuid, Storage.StoragePoolType srcPoolType, String srcBackingFilePath, boolean copySrcTemplate, ScopeType scopeType, Long srcPoolClusterId) { this.srcPoolUuid = srcPoolUuid; this.srcPoolType = srcPoolType; this.type = Type.LinkedClone; this.scopeType = scopeType; this.srcBackingFilePath = srcBackingFilePath; this.copySrcTemplate = copySrcTemplate; + this.srcPoolClusterId = srcPoolClusterId; } - public MigrationOptions(String srcPoolUuid, Storage.StoragePoolType srcPoolType, String srcVolumeUuid, ScopeType scopeType) { + public MigrationOptions(String srcPoolUuid, Storage.StoragePoolType srcPoolType, String srcVolumeUuid, ScopeType scopeType, Long srcPoolClusterId) { this.srcPoolUuid = srcPoolUuid; this.srcPoolType = srcPoolType; this.type = Type.FullClone; this.scopeType = scopeType; this.srcVolumeUuid = srcVolumeUuid; + this.srcPoolClusterId = srcPoolClusterId; } public String getSrcPoolUuid() { @@ -63,6 +66,10 @@ public Storage.StoragePoolType getSrcPoolType() { return srcPoolType; } + public Long getSrcPoolClusterId() { + return srcPoolClusterId; + } + public ScopeType getScopeType() { return scopeType; } public String getSrcBackingFilePath() { diff --git a/api/src/main/java/com/cloud/storage/Snapshot.java b/api/src/main/java/com/cloud/storage/Snapshot.java index fc919e442b2e..c0a7b812ed9e 100644 --- a/api/src/main/java/com/cloud/storage/Snapshot.java +++ b/api/src/main/java/com/cloud/storage/Snapshot.java @@ -48,7 +48,7 @@ public boolean equals(String snapshotType) { } public enum State { - Allocated, Creating, CreatedOnPrimary, BackingUp, BackedUp, Copying, Destroying, Destroyed, + Allocated, Creating, CreatedOnPrimary, BackingUp, BackedUp, Copying, Destroying, Destroyed, Hidden, //it's a state, user can't see the snapshot from ui, while the snapshot may still exist on the storage Error; diff --git a/api/src/main/java/com/cloud/storage/Storage.java b/api/src/main/java/com/cloud/storage/Storage.java index c997f5e1dbfd..5b3e97698fda 100644 --- a/api/src/main/java/com/cloud/storage/Storage.java +++ b/api/src/main/java/com/cloud/storage/Storage.java @@ -16,14 +16,10 @@ // under the License. package com.cloud.storage; +import org.apache.commons.lang.NotImplementedException; + import java.util.ArrayList; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; -import java.util.Objects; - -import org.apache.commons.lang.NotImplementedException; -import org.apache.commons.lang3.StringUtils; public class Storage { public static enum ImageFormat { @@ -34,6 +30,7 @@ public static enum ImageFormat { OVA(true, true, true, "ova"), VHDX(true, true, true, "vhdx"), BAREMETAL(false, false, false, "BAREMETAL"), + EXTERNAL(false, false, false, "EXTERNAL"), VMDK(true, true, false, "vmdk"), VDI(true, true, false, "vdi"), TAR(false, false, false, "tar"), @@ -131,7 +128,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 */ @@ -139,6 +136,21 @@ public static enum TemplateType { ISODISK /* Template corresponding to a iso (non root disk) present in an OVA */ } + public enum EncryptionSupport { + /** + * Encryption not supported. + */ + Unsupported, + /** + * Will use hypervisor encryption driver (qemu -> luks) + */ + Hypervisor, + /** + * Storage pool handles encryption and just provides an encrypted volume + */ + Storage + } + /** * StoragePoolTypes carry some details about the format and capabilities of a storage pool. While not necessarily a * 1:1 with PrimaryDataStoreDriver (and for KVM agent, KVMStoragePool and StorageAdaptor) implementations, it is @@ -150,61 +162,37 @@ public static enum TemplateType { * ensure this is available on the agent side as well. This is best done by defining the StoragePoolType in a common * package available on both management server and agent plugin jars. */ - public static class StoragePoolType { - private static final Map map = new LinkedHashMap<>(); - - public static final StoragePoolType Filesystem = new StoragePoolType("Filesystem", false, true, true); - public static final StoragePoolType NetworkFilesystem = new StoragePoolType("NetworkFilesystem", true, true, true); - public static final StoragePoolType IscsiLUN = new StoragePoolType("IscsiLUN", true, false, false); - public static final StoragePoolType Iscsi = new StoragePoolType("Iscsi", true, false, false); - public static final StoragePoolType ISO = new StoragePoolType("ISO", false, false, false); - public static final StoragePoolType LVM = new StoragePoolType("LVM", false, false, false); - public static final StoragePoolType CLVM = new StoragePoolType("CLVM", true, false, false); - public static final StoragePoolType RBD = new StoragePoolType("RBD", true, true, false); - public static final StoragePoolType SharedMountPoint = new StoragePoolType("SharedMountPoint", true, true, true); - public static final StoragePoolType VMFS = new StoragePoolType("VMFS", true, true, false); - public static final StoragePoolType PreSetup = new StoragePoolType("PreSetup", true, true, false); - public static final StoragePoolType EXT = new StoragePoolType("EXT", false, true, false); - public static final StoragePoolType OCFS2 = new StoragePoolType("OCFS2", true, false, false); - public static final StoragePoolType SMB = new StoragePoolType("SMB", true, false, false); - public static final StoragePoolType Gluster = new StoragePoolType("Gluster", true, false, false); - public static final StoragePoolType PowerFlex = new StoragePoolType("PowerFlex", true, true, true); - public static final StoragePoolType ManagedNFS = new StoragePoolType("ManagedNFS", true, false, false); - public static final StoragePoolType Linstor = new StoragePoolType("Linstor", true, true, false); - public static final StoragePoolType DatastoreCluster = new StoragePoolType("DatastoreCluster", true, true, false); - public static final StoragePoolType StorPool = new StoragePoolType("StorPool", true,true,true); - public static final StoragePoolType FiberChannel = new StoragePoolType("FiberChannel", true,true,false); - - - private final String name; + public static enum StoragePoolType { + Filesystem(false, true, EncryptionSupport.Hypervisor), // local directory + NetworkFilesystem(true, true, EncryptionSupport.Hypervisor), // NFS + IscsiLUN(true, false, EncryptionSupport.Unsupported), // shared LUN, with a clusterfs overlay + Iscsi(true, false, EncryptionSupport.Unsupported), // for e.g., ZFS Comstar + ISO(false, false, EncryptionSupport.Unsupported), // for iso image + LVM(false, false, EncryptionSupport.Unsupported), // XenServer local LVM SR + CLVM(true, false, EncryptionSupport.Unsupported), + RBD(true, true, EncryptionSupport.Unsupported), // http://libvirt.org/storage.html#StorageBackendRBD + SharedMountPoint(true, true, EncryptionSupport.Hypervisor), + VMFS(true, true, EncryptionSupport.Unsupported), // VMware VMFS storage + PreSetup(true, true, EncryptionSupport.Unsupported), // for XenServer, Storage Pool is set up by customers. + EXT(false, true, EncryptionSupport.Unsupported), // XenServer local EXT SR + OCFS2(true, false, EncryptionSupport.Unsupported), + SMB(true, false, EncryptionSupport.Unsupported), + Gluster(true, false, EncryptionSupport.Unsupported), + PowerFlex(true, true, EncryptionSupport.Hypervisor), // Dell EMC PowerFlex/ScaleIO (formerly VxFlexOS) + ManagedNFS(true, false, EncryptionSupport.Unsupported), + Linstor(true, true, EncryptionSupport.Storage), + DatastoreCluster(true, true, EncryptionSupport.Unsupported), // for VMware, to abstract pool of clusters + StorPool(true, true, EncryptionSupport.Hypervisor), + FiberChannel(true, true, EncryptionSupport.Unsupported); // Fiber Channel Pool for KVM hypervisors is used to find the volume by WWN value (/dev/disk/by-id/wwn-) + private final boolean shared; private final boolean overProvisioning; - private final boolean encryption; + private final EncryptionSupport encryption; - /** - * New StoragePoolType, set the name to check with it in Dao (Note: Do not register it into the map of pool types). - * @param name name of the StoragePoolType. - */ - public StoragePoolType(String name) { - this.name = name; - this.shared = false; - this.overProvisioning = false; - this.encryption = false; - } - - /** - * Define a new StoragePoolType, and register it into the map of pool types known to the management server. - * @param name Simple unique name of the StoragePoolType. - * @param shared Storage pool is shared/accessible to multiple hypervisors - * @param overProvisioning Storage pool supports overProvisioning - * @param encryption Storage pool supports encrypted volumes - */ - public StoragePoolType(String name, boolean shared, boolean overProvisioning, boolean encryption) { - this.name = name; + StoragePoolType(boolean shared, boolean overProvisioning, EncryptionSupport encryption) { this.shared = shared; this.overProvisioning = overProvisioning; this.encryption = encryption; - addStoragePoolType(this); } public boolean isShared() { @@ -216,49 +204,11 @@ public boolean supportsOverProvisioning() { } public boolean supportsEncryption() { - return encryption; - } - - private static void addStoragePoolType(StoragePoolType storagePoolType) { - map.putIfAbsent(storagePoolType.name, storagePoolType); - } - - public static StoragePoolType[] values() { - return map.values().toArray(StoragePoolType[]::new).clone(); - } - - public static StoragePoolType valueOf(String name) { - if (StringUtils.isBlank(name)) { - return null; - } - - StoragePoolType storage = map.get(name); - if (storage == null) { - throw new IllegalArgumentException("StoragePoolType '" + name + "' not found"); - } - return storage; + return encryption == EncryptionSupport.Hypervisor || encryption == EncryptionSupport.Storage; } - @Override - public String toString() { - return name; - } - - public String name() { - return name; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - StoragePoolType that = (StoragePoolType) o; - return Objects.equals(name, that.name); - } - - @Override - public int hashCode() { - return Objects.hash(name); + public EncryptionSupport encryptionSupportMode() { + return encryption; } } diff --git a/api/src/main/java/com/cloud/storage/StorageService.java b/api/src/main/java/com/cloud/storage/StorageService.java index c3609cfd8eea..a29c8f6aecef 100644 --- a/api/src/main/java/com/cloud/storage/StorageService.java +++ b/api/src/main/java/com/cloud/storage/StorageService.java @@ -21,6 +21,8 @@ import java.util.Map; import org.apache.cloudstack.api.command.admin.storage.CancelPrimaryStorageMaintenanceCmd; +import org.apache.cloudstack.api.command.admin.storage.ChangeStoragePoolScopeCmd; +import org.apache.cloudstack.api.command.admin.storage.ConfigureStorageAccessCmd; import org.apache.cloudstack.api.command.admin.storage.CreateSecondaryStagingStoreCmd; import org.apache.cloudstack.api.command.admin.storage.CreateStoragePoolCmd; import org.apache.cloudstack.api.command.admin.storage.DeleteImageStoreCmd; @@ -29,11 +31,13 @@ import org.apache.cloudstack.api.command.admin.storage.DeleteSecondaryStagingStoreCmd; import org.apache.cloudstack.api.command.admin.storage.SyncStoragePoolCmd; import org.apache.cloudstack.api.command.admin.storage.UpdateObjectStoragePoolCmd; +import org.apache.cloudstack.api.command.admin.storage.UpdateImageStoreCmd; import org.apache.cloudstack.api.command.admin.storage.UpdateStoragePoolCmd; import com.cloud.exception.DiscoveryException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceInUseException; import com.cloud.exception.ResourceUnavailableException; import org.apache.cloudstack.api.command.admin.storage.heuristics.CreateSecondaryStorageSelectorCmd; @@ -92,6 +96,12 @@ public interface StorageService { StoragePool updateStoragePool(UpdateStoragePoolCmd cmd) throws IllegalArgumentException; + StoragePool enablePrimaryStoragePool(Long id); + + StoragePool disablePrimaryStoragePool(Long id); + + boolean configureStorageAccess(ConfigureStorageAccessCmd cmd); + StoragePool getStoragePool(long id); boolean deleteImageStore(DeleteImageStoreCmd cmd); @@ -110,6 +120,8 @@ public interface StorageService { */ ImageStore migrateToObjectStore(String name, String url, String providerName, Map details) throws DiscoveryException; + ImageStore updateImageStore(UpdateImageStoreCmd cmd); + ImageStore updateImageStoreStatus(Long id, Boolean readonly); void updateStorageCapabilities(Long poolId, boolean failOnChecks); @@ -122,9 +134,11 @@ public interface StorageService { void removeSecondaryStorageHeuristic(RemoveSecondaryStorageSelectorCmd cmd); - ObjectStore discoverObjectStore(String name, String url, String providerName, Map details) throws IllegalArgumentException, DiscoveryException, InvalidParameterValueException; + ObjectStore discoverObjectStore(String name, String url, Long size, String providerName, Map details) throws IllegalArgumentException, DiscoveryException, InvalidParameterValueException; boolean deleteObjectStore(DeleteObjectStoragePoolCmd cmd); ObjectStore updateObjectStore(Long id, UpdateObjectStoragePoolCmd cmd); + + void changeStoragePoolScope(ChangeStoragePoolScopeCmd cmd) throws IllegalArgumentException, InvalidParameterValueException, PermissionDeniedException; } diff --git a/api/src/main/java/com/cloud/storage/StorageStats.java b/api/src/main/java/com/cloud/storage/StorageStats.java index a474b23489cd..502e2aaae40a 100644 --- a/api/src/main/java/com/cloud/storage/StorageStats.java +++ b/api/src/main/java/com/cloud/storage/StorageStats.java @@ -26,4 +26,7 @@ public interface StorageStats { * @return bytes capacity of the storage server */ public long getCapacityBytes(); + + Long getCapacityIops(); + Long getUsedIops(); } diff --git a/api/src/main/java/com/cloud/storage/Upload.java b/api/src/main/java/com/cloud/storage/Upload.java index 59d203ac73ae..4e696e877cc8 100644 --- a/api/src/main/java/com/cloud/storage/Upload.java +++ b/api/src/main/java/com/cloud/storage/Upload.java @@ -40,7 +40,7 @@ public static enum Status { } public static enum Type { - VOLUME, TEMPLATE, ISO + VOLUME, SNAPSHOT, TEMPLATE, ISO } public static enum Mode { diff --git a/api/src/main/java/com/cloud/storage/VMTemplateStorageResourceAssoc.java b/api/src/main/java/com/cloud/storage/VMTemplateStorageResourceAssoc.java index f43d5331222f..db702a61f2bc 100644 --- a/api/src/main/java/com/cloud/storage/VMTemplateStorageResourceAssoc.java +++ b/api/src/main/java/com/cloud/storage/VMTemplateStorageResourceAssoc.java @@ -17,6 +17,7 @@ package com.cloud.storage; import java.util.Date; +import java.util.List; import org.apache.cloudstack.api.InternalIdentity; @@ -25,6 +26,8 @@ public static enum Status { UNKNOWN, DOWNLOAD_ERROR, NOT_DOWNLOADED, DOWNLOAD_IN_PROGRESS, DOWNLOADED, ABANDONED, UPLOADED, NOT_UPLOADED, UPLOAD_ERROR, UPLOAD_IN_PROGRESS, CREATING, CREATED, BYPASSED } + List PENDING_DOWNLOAD_STATES = List.of(Status.NOT_DOWNLOADED, Status.DOWNLOAD_IN_PROGRESS); + String getInstallPath(); long getTemplateId(); diff --git a/api/src/main/java/com/cloud/storage/Volume.java b/api/src/main/java/com/cloud/storage/Volume.java index 308ed2544ed0..c7fbdb0a5445 100644 --- a/api/src/main/java/com/cloud/storage/Volume.java +++ b/api/src/main/java/com/cloud/storage/Volume.java @@ -30,6 +30,8 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, BasedOn, StateObject, Displayable { + static final long DISK_OFFERING_SUITABILITY_CHECK_VOLUME_ID = -1; + // Managed storage volume parameters (specified in the compute/disk offering for PowerFlex) String BANDWIDTH_LIMIT_IN_MBPS = "bandwidthLimitInMbps"; String IOPS_LIMIT = "iopsLimit"; @@ -269,11 +271,13 @@ enum Event { void setExternalUuid(String externalUuid); - public Long getPassphraseId(); + Long getPassphraseId(); + + void setPassphraseId(Long id); - public void setPassphraseId(Long id); + String getEncryptFormat(); - public String getEncryptFormat(); + void setEncryptFormat(String encryptFormat); - public void setEncryptFormat(String encryptFormat); + boolean isDeleteProtection(); } diff --git a/api/src/main/java/com/cloud/storage/VolumeApiService.java b/api/src/main/java/com/cloud/storage/VolumeApiService.java index 8d5f7892f102..1a9bcc6ee98b 100644 --- a/api/src/main/java/com/cloud/storage/VolumeApiService.java +++ b/api/src/main/java/com/cloud/storage/VolumeApiService.java @@ -22,9 +22,16 @@ import java.util.List; import java.util.Map; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.offering.DiskOffering; +import com.cloud.user.Account; +import com.cloud.utils.Pair; +import com.cloud.utils.fsm.NoTransitionException; + import org.apache.cloudstack.api.command.user.volume.AssignVolumeCmd; import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd; import org.apache.cloudstack.api.command.user.volume.ChangeOfferingForVolumeCmd; +import org.apache.cloudstack.api.command.user.volume.CheckAndRepairVolumeCmd; import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd; import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd; import org.apache.cloudstack.api.command.user.volume.ExtractVolumeCmd; @@ -35,13 +42,9 @@ import org.apache.cloudstack.api.response.GetUploadParamsResponse; import org.apache.cloudstack.framework.config.ConfigKey; -import com.cloud.exception.ResourceAllocationException; -import com.cloud.user.Account; -import com.cloud.utils.fsm.NoTransitionException; - public interface VolumeApiService { - ConfigKey ConcurrentMigrationsThresholdPerDatastore = new ConfigKey("Advanced" + ConfigKey ConcurrentMigrationsThresholdPerDatastore = new ConfigKey<>("Advanced" , Long.class , "concurrent.migrations.per.target.datastore" , "0" @@ -49,13 +52,13 @@ public interface VolumeApiService { , true // not sure if this is to be dynamic , ConfigKey.Scope.Global); - ConfigKey UseHttpsToUpload = new ConfigKey("Advanced", + ConfigKey UseHttpsToUpload = new ConfigKey<>("Advanced", Boolean.class, "use.https.to.upload", "true", - "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 @@ -83,7 +86,7 @@ public interface VolumeApiService { * @param cmd * the API command wrapping the criteria * @return the volume object - * @throws ResourceAllocationException + * @throws ResourceAllocationException no capacity to allocate the new volume size */ Volume resizeVolume(ResizeVolumeCmd cmd) throws ResourceAllocationException; @@ -100,18 +103,24 @@ public interface VolumeApiService { boolean deleteVolume(long volumeId, Account caller); + Volume changeDiskOfferingForVolumeInternal(Long volumeId, Long newDiskOfferingId, Long newSize, Long newMinIops, Long newMaxIops, boolean autoMigrateVolume, boolean shrinkOk) throws ResourceAllocationException; + Volume attachVolumeToVM(AttachVolumeCmd command); + Volume attachVolumeToVM(Long vmId, Long volumeId, Long deviceId, Boolean allowAttachForSharedFS); + Volume detachVolumeViaDestroyVM(long vmId, long volumeId); Volume detachVolumeFromVM(DetachVolumeCmd cmd); - Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account, boolean quiescevm, Snapshot.LocationType locationType, boolean asyncBackup, Map tags, List zoneIds) + Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account, boolean quiescevm, Snapshot.LocationType locationType, boolean asyncBackup, Map tags, List zoneIds, List poolIds, Boolean useStorageReplication) throws ResourceAllocationException; - Snapshot allocSnapshot(Long volumeId, Long policyId, String snapshotName, Snapshot.LocationType locationType, List zoneIds) throws ResourceAllocationException; + Snapshot allocSnapshot(Long volumeId, Long policyId, String snapshotName, Snapshot.LocationType locationType, List zoneIds, List storagePoolIds, Boolean useStorageReplication) throws ResourceAllocationException; - Volume updateVolume(long volumeId, String path, String state, Long storageId, Boolean displayVolume, String customId, long owner, String chainInfo, String name); + Volume updateVolume(long volumeId, String path, String state, Long storageId, + Boolean displayVolume, Boolean deleteProtection, + String customId, long owner, String chainInfo, String name); /** * Extracts the volume to a particular location. @@ -128,16 +137,16 @@ Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account acc void updateDisplay(Volume volume, Boolean displayVolume); - Snapshot allocSnapshotForVm(Long vmId, Long volumeId, String snapshotName) throws ResourceAllocationException; + Snapshot allocSnapshotForVm(Long vmId, Long volumeId, String snapshotName, Long vmSnapshotId) throws ResourceAllocationException; /** - * Checks if the target storage supports the disk offering. + * Checks if the storage pool supports the disk offering tags. * This validation is consistent with the mechanism used to select a storage pool to deploy a volume when a virtual machine is deployed or when a data disk is allocated. * * The scenarios when this method returns true or false is presented in the following table. * * - * + * * * * @@ -161,7 +170,17 @@ Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account acc * *
#Disk offering tagsStorage tagsDoes the storage support the disk offering?#Disk offering diskOfferingTagsStorage diskOfferingTagsDoes the storage support the disk offering?
*/ - boolean doesTargetStorageSupportDiskOffering(StoragePool destPool, String diskOfferingTags); + boolean doesStoragePoolSupportDiskOffering(StoragePool destPool, DiskOffering diskOffering); + + /** + * Checks if the storage pool supports the required disk offering tags + * destPool the storage pool to check the disk offering tags + * diskOfferingTags the tags that should be supported + * return whether the tags are supported in the storage pool + */ + boolean doesStoragePoolSupportDiskOfferingTags(StoragePool destPool, String diskOfferingTags); + + boolean validateConditionsToReplaceDiskOfferingOfVolume(Volume volume, DiskOffering newDiskOffering, StoragePool destPool); Volume destroyVolume(long volumeId, Account caller, boolean expunge, boolean forceExpunge); @@ -173,9 +192,15 @@ Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account acc boolean validateVolumeSizeInBytes(long size); + void validateDestroyVolume(Volume volume, Account caller, boolean expunge, boolean forceExpunge); + Volume changeDiskOfferingForVolume(ChangeOfferingForVolumeCmd cmd) throws ResourceAllocationException; void publishVolumeCreationUsageEvent(Volume volume); boolean stateTransitTo(Volume vol, Volume.Event event) throws NoTransitionException; + + Pair checkAndRepairVolume(CheckAndRepairVolumeCmd cmd) throws ResourceAllocationException; + + Long getVolumePhysicalSize(Storage.ImageFormat format, String path, String chainInfo); } 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 0893f337ce2f..d52e645ec799 100644 --- a/api/src/main/java/com/cloud/storage/snapshot/SnapshotApiService.java +++ b/api/src/main/java/com/cloud/storage/snapshot/SnapshotApiService.java @@ -21,6 +21,7 @@ import org.apache.cloudstack.api.command.user.snapshot.CopySnapshotCmd; import org.apache.cloudstack.api.command.user.snapshot.CreateSnapshotPolicyCmd; import org.apache.cloudstack.api.command.user.snapshot.DeleteSnapshotPoliciesCmd; +import org.apache.cloudstack.api.command.user.snapshot.ExtractSnapshotCmd; import org.apache.cloudstack.api.command.user.snapshot.ListSnapshotPoliciesCmd; import org.apache.cloudstack.api.command.user.snapshot.ListSnapshotsCmd; import org.apache.cloudstack.api.command.user.snapshot.UpdateSnapshotPolicyCmd; @@ -84,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); @@ -106,6 +107,16 @@ Snapshot allocSnapshot(Long volumeId, Long policyId, String snapshotName, Snapsh */ Snapshot createSnapshot(Long volumeId, Long policyId, Long snapshotId, Account snapshotOwner); + /** + * Extracts the snapshot to a particular location. + * + * @param cmd + * the command specifying url (where the snapshot needs to be extracted to), zoneId (zone where the snapshot exists) and + * id (the id of the snapshot) + * + */ + String extractSnapshot(ExtractSnapshotCmd cmd); + /** * Archives a snapshot from primary storage to secondary storage. * @param id Snapshot ID 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/template/TemplateApiService.java b/api/src/main/java/com/cloud/template/TemplateApiService.java index 5b494c308c3c..6138f24c92b0 100644 --- a/api/src/main/java/com/cloud/template/TemplateApiService.java +++ b/api/src/main/java/com/cloud/template/TemplateApiService.java @@ -58,10 +58,23 @@ public interface TemplateApiService { VirtualMachineTemplate prepareTemplate(long templateId, long zoneId, Long storageId); + /** + * Detach ISO from VM + * @param vmId id of the VM + * @param isoId id of the ISO (when passed). If it is not passed, it will get it from user_vm table + * @param extraParams forced, isVirtualRouter + * @return true when operation succeeds, false if not + */ + boolean detachIso(long vmId, Long isoId, Boolean... extraParams); - boolean detachIso(long vmId, boolean forced); - - boolean attachIso(long isoId, long vmId, boolean forced); + /** + * Attach ISO to a VM + * @param isoId id of the ISO to attach + * @param vmId id of the VM to attach the ISO to + * @param extraParams: forced, isVirtualRouter + * @return true when operation succeeds, false if not + */ + boolean attachIso(long isoId, long vmId, Boolean... extraParams); /** * Deletes a template diff --git a/api/src/main/java/com/cloud/template/VirtualMachineTemplate.java b/api/src/main/java/com/cloud/template/VirtualMachineTemplate.java index 1f8cef0365b1..b8c646048b97 100644 --- a/api/src/main/java/com/cloud/template/VirtualMachineTemplate.java +++ b/api/src/main/java/com/cloud/template/VirtualMachineTemplate.java @@ -19,6 +19,7 @@ import java.util.Date; import java.util.Map; +import com.cloud.cpu.CPU; import com.cloud.user.UserData; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.api.Identity; @@ -144,8 +145,14 @@ public enum TemplateFilter { boolean isDeployAsIs(); + boolean isForCks(); + Long getUserDataId(); UserData.UserDataOverridePolicy getUserDataOverridePolicy(); + CPU.CPUArch getArch(); + + Long getExtensionId(); + } diff --git a/api/src/main/java/com/cloud/user/Account.java b/api/src/main/java/com/cloud/user/Account.java index bb9838f137a9..1cc5eae64a66 100644 --- a/api/src/main/java/com/cloud/user/Account.java +++ b/api/src/main/java/com/cloud/user/Account.java @@ -71,6 +71,7 @@ public static Type getFromValue(Integer type){ } public static final long ACCOUNT_ID_SYSTEM = 1; + public static final long ACCOUNT_ID_ADMIN = 2; public String getAccountName(); @@ -93,4 +94,8 @@ public static Type getFromValue(Integer type){ boolean isDefault(); + public void setApiKeyAccess(Boolean apiKeyAccess); + + public Boolean getApiKeyAccess(); + } diff --git a/api/src/main/java/com/cloud/user/AccountService.java b/api/src/main/java/com/cloud/user/AccountService.java index 63f5455cfd09..30919e5b782f 100644 --- a/api/src/main/java/com/cloud/user/AccountService.java +++ b/api/src/main/java/com/cloud/user/AccountService.java @@ -19,13 +19,15 @@ import java.util.List; import java.util.Map; +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; @@ -34,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 { @@ -57,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); @@ -86,13 +98,15 @@ User createUser(String userName, String password, String firstName, String lastN boolean isDomainAdmin(Long accountId); + boolean isResourceDomainAdmin(Long accountId); + boolean isNormalUser(long accountId); User getActiveUserByRegistrationToken(String registrationToken); void markUserRegistered(long userId); - public String[] createApiKeyAndSecretKey(RegisterCmd cmd); + ApiKeyPair createApiKeyAndSecretKey(RegisterUserKeysCmd cmd); public String[] createApiKeyAndSecretKey(final long userId); @@ -112,11 +126,17 @@ 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; - Long finalyzeAccountId(String accountName, Long domainId, Long projectId, boolean enabledOnly); + void validateAccountHasAccessToResource(Account account, AccessType accessType, Object resource); + + void validateCallingUserHasAccessToDesiredUser(Long userId); + + Long finalizeAccountId(String accountName, Long domainId, Long projectId, boolean enabledOnly); /** * returns the user account object for a given user id @@ -125,9 +145,15 @@ User createUser(String userName, String password, String firstName, String lastN */ UserAccount getUserAccountById(Long userId); - public Map getKeys(GetUserKeysCmd cmd); + Pair> getKeys(GetUserKeysCmd cmd); - public Map getKeys(Long userId); + ListResponse listKeys(ListUserKeysCmd cmd); + + List listKeyRules(ListUserKeyRulesCmd cmd); + + void deleteApiKey(DeleteUserKeysCmd cmd); + + void deleteApiKey(ApiKeyPair id); /** * Lists user two-factor authentication provider plugins @@ -142,4 +168,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 f2d87a4390df..738e593582b4 100644 --- a/api/src/main/java/com/cloud/user/ResourceLimitService.java +++ b/api/src/main/java/com/cloud/user/ResourceLimitService.java @@ -18,22 +18,47 @@ import java.util.List; +import org.apache.cloudstack.api.response.AccountResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.framework.config.ConfigKey; + import com.cloud.configuration.Resource.ResourceType; import com.cloud.configuration.ResourceCount; import com.cloud.configuration.ResourceLimit; import com.cloud.domain.Domain; import com.cloud.exception.ResourceAllocationException; -import org.apache.cloudstack.framework.config.ConfigKey; -import org.apache.cloudstack.user.ResourceReservation; +import com.cloud.offering.DiskOffering; +import com.cloud.offering.ServiceOffering; +import com.cloud.template.VirtualMachineTemplate; 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", - "Time (in seconds) to wait before running resource recalculation and fixing task. Default is 300 seconds, Setting this to 0 disables execution of the task", false); + "Time (in seconds) to wait before running resource recalculation and fixing tasks like stale resource reservation cleanup" + + ". Default is 300 seconds, Setting this to 0 disables execution of the task", true); + static final ConfigKey ResourceReservationCleanupDelay = new ConfigKey<>("Advanced", Long.class, "resource.reservation.cleanup.delay", "3600", + "Time (in seconds) after which a resource reservation gets deleted. Default is 3600 seconds, Setting this to 0 disables execution of the task", true); + static final ConfigKey ResourceLimitHostTags = new ConfigKey<>("Advanced", String.class, "resource.limit.host.tags", "", + "A comma-separated list of tags for host resource limits", true); + static final ConfigKey ResourceLimitStorageTags = new ConfigKey<>("Advanced", String.class, "resource.limit.storage.tags", "", + "A comma-separated list of tags for storage resource limits", true); + static final ConfigKey DefaultMaxAccountProjects = new ConfigKey<>("Account Defaults",Long.class,"max.account.projects","10", + "The default maximum number of projects that can be created for an account",false); + static final ConfigKey DefaultMaxDomainProjects = new ConfigKey<>("Domain Defaults",Long.class,"max.domain.projects","50", + "The default maximum number of projects that can be created for a domain",false); + static final ConfigKey DefaultMaxAccountGpus = new ConfigKey<>("Account Defaults",Long.class,"max.account.gpus","20", + "The default maximum number of GPU devices that can be used for an account", false); + static final ConfigKey DefaultMaxDomainGpus = new ConfigKey<>("Domain Defaults",Long.class,"max.domain.gpus","20", + "The default maximum number of GPU devices that can be used for a domain", false); + static final ConfigKey DefaultMaxProjectGpus = new ConfigKey<>("Project Defaults",Long.class,"max.project.gpus","20", + "The default maximum number of GPU devices that can be used for a project", false); + + static final List HostTagsSupportingTypes = List.of(ResourceType.user_vm, ResourceType.cpu, ResourceType.memory, ResourceType.gpu); + static final List StorageTagsSupportingTypes = List.of(ResourceType.volume, ResourceType.primary_storage); /** * Updates an existing resource limit with the specified details. If a limit doesn't exist, will create one. @@ -46,22 +71,27 @@ public interface ResourceLimitService { * TODO * @param max * TODO + * @param tag + * tag for the resource type * * @return the updated/created resource limit */ - ResourceLimit updateResourceLimit(Long accountId, Long domainId, Integer resourceType, Long max); + ResourceLimit updateResourceLimit(Long accountId, Long domainId, Integer resourceType, Long max, String tag); /** * Updates an existing resource count details for the account/domain * * @param accountId - * TODO + * Id of the account for which resource recalculation to be done * @param domainId - * TODO + * Id of the domain for which resource recalculation to be doneDO * @param typeId - * TODO + * type of the resource for which recalculation to be done + * @param tag + * tag for the resource type for which recalculation to be done * @return the updated/created resource counts */ + List recalculateResourceCount(Long accountId, Long domainId, Integer typeId, String tag); List recalculateResourceCount(Long accountId, Long domainId, Integer typeId); /** @@ -77,7 +107,7 @@ public interface ResourceLimitService { * TODO * @return a list of limits that match the criteria */ - public List searchForLimits(Long id, Long accountId, Long domainId, ResourceType resourceType, Long startIndex, Long pageSizeVal); + public List searchForLimits(Long id, Long accountId, Long domainId, ResourceType resourceType, String tag, Long startIndex, Long pageSizeVal); /** * Finds the resource limit for a specified account and type. If the account has an infinite limit, will check @@ -85,9 +115,10 @@ public interface ResourceLimitService { * * @param account * @param type + * @param tag * @return resource limit */ - public long findCorrectResourceLimitForAccount(Account account, ResourceType type); + public long findCorrectResourceLimitForAccount(Account account, ResourceType type, String tag); /** * This call should be used when we have already queried resource limit for an account. This is to handle @@ -105,9 +136,10 @@ public interface ResourceLimitService { * * @param domain * @param type + * @param tag * @return resource limit */ - public long findCorrectResourceLimitForDomain(Domain domain, ResourceType type); + public long findCorrectResourceLimitForDomain(Domain domain, ResourceType type, String tag); /** * Finds the default resource limit for a specified type. @@ -122,9 +154,10 @@ public interface ResourceLimitService { * * @param domain * @param type + * @param tag * @return resource limit */ - public long findCorrectResourceLimitForAccountAndDomain(Account account, Domain domain, ResourceType type); + public long findCorrectResourceLimitForAccountAndDomain(Account account, Domain domain, ResourceType type, String tag); /** * Increments the resource count @@ -134,6 +167,7 @@ public interface ResourceLimitService { * @param delta */ public void incrementResourceCount(long accountId, ResourceType type, Long... delta); + public void incrementResourceCountWithTag(long accountId, ResourceType type, String tag, Long... delta); /** * Decrements the resource count @@ -143,6 +177,7 @@ public interface ResourceLimitService { * @param delta */ public void decrementResourceCount(long accountId, ResourceType type, Long... delta); + public void decrementResourceCountWithTag(long accountId, ResourceType type, String tag, Long... delta); /** * Checks if a limit has been exceeded for an account @@ -155,15 +190,17 @@ public interface ResourceLimitService { * @throws ResourceAllocationException */ 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; /** * Gets the count of resources for a resource type and account * * @param account * @param type + * @param tag * @return count of resources */ - public long getResourceCount(Account account, ResourceType type); + public long getResourceCount(Account account, ResourceType type, String tag); /** * Checks if a limit has been exceeded for an account if displayResource flag is on @@ -208,15 +245,53 @@ public interface ResourceLimitService { */ void decrementResourceCount(long accountId, ResourceType type, Boolean displayResource, Long... delta); - /** - * Adds a reservation that will be counted in subsequent calls to {count}getResourceCount{code} until {code}this[code} - * is closed. It will create a reservation record that will be counted when resource limits are checked. - * @param account The account for which the reservation is. - * @param displayResource whether this resource is shown to users at all (if not it is not counted to limits) - * @param type resource type - * @param delta amount to reserve (will not be <+ 0) - * @return a {code}AutoClosable{Code} object representing the resource the user needs - */ - ResourceReservation getReservation(Account account, Boolean displayResource, ResourceType type, Long delta) throws ResourceAllocationException; + List getResourceLimitHostTags(); + List getResourceLimitHostTags(ServiceOffering serviceOffering, VirtualMachineTemplate template); + List getResourceLimitStorageTags(); + 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 checkVolumeResourceLimitForDiskOfferingChange(Account owner, Boolean display, Long currentSize, Long newSize, + DiskOffering currentOffering, DiskOffering newOffering) throws ResourceAllocationException; + + void checkPrimaryStorageResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering) throws ResourceAllocationException; + + void incrementVolumeResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering); + void decrementVolumeResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering); + + void updateVmResourceCountForTemplateChange(long accountId, Boolean display, ServiceOffering offering, VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate); + + void updateVmResourceCountForServiceOfferingChange(long accountId, Boolean display, Long currentCpu, Long newCpu, Long currentMemory, + Long newMemory, + ServiceOffering currentOffering, ServiceOffering newOffering, + VirtualMachineTemplate template); + + void updateVolumeResourceCountForDiskOfferingChange(long accountId, Boolean display, Long currentSize, Long newSize, + DiskOffering currentDiskOffering, DiskOffering newDiskOffering); + + 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 checkVmResourceLimitsForServiceOfferingChange(Account owner, Boolean display, Long currentCpu, Long newCpu, + Long currentMemory, Long newMemory, ServiceOffering currentOffering, ServiceOffering newOffering, VirtualMachineTemplate template) throws ResourceAllocationException; + + void checkVmResourceLimitsForTemplateChange(Account owner, Boolean display, ServiceOffering offering, + VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate) 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); } diff --git a/api/src/main/java/com/cloud/user/User.java b/api/src/main/java/com/cloud/user/User.java index 422e264f10be..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); @@ -94,4 +86,9 @@ public enum Source { public boolean isUser2faEnabled(); public String getKeyFor2fa(); + + public void setApiKeyAccess(Boolean apiKeyAccess); + + public Boolean getApiKeyAccess(); + } 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/user/UserData.java b/api/src/main/java/com/cloud/user/UserData.java index fa0c50473c0d..13a3c74f3679 100644 --- a/api/src/main/java/com/cloud/user/UserData.java +++ b/api/src/main/java/com/cloud/user/UserData.java @@ -29,4 +29,5 @@ public enum UserDataOverridePolicy { String getUserData(); String getParams(); + boolean isForCks(); } diff --git a/api/src/main/java/com/cloud/uservm/UserVm.java b/api/src/main/java/com/cloud/uservm/UserVm.java index e30f5e030544..9035d2903c9a 100644 --- a/api/src/main/java/com/cloud/uservm/UserVm.java +++ b/api/src/main/java/com/cloud/uservm/UserVm.java @@ -48,4 +48,6 @@ public interface UserVm extends VirtualMachine, ControlledEntity { void setAccountId(long accountId); public boolean isDisplayVm(); + + String getUserVmType(); } diff --git a/api/src/main/java/com/cloud/vm/NicProfile.java b/api/src/main/java/com/cloud/vm/NicProfile.java index 3f37331b1e44..a0c80ceb1bfb 100644 --- a/api/src/main/java/com/cloud/vm/NicProfile.java +++ b/api/src/main/java/com/cloud/vm/NicProfile.java @@ -62,6 +62,7 @@ public class NicProfile implements InternalIdentity, Serializable { String iPv4Dns1; String iPv4Dns2; String requestedIPv4; + boolean ipv4AllocationRaceCheck; // IPv6 String iPv6Address; @@ -405,6 +406,13 @@ public void setMtu(Integer mtu) { this.mtu = mtu; } + public boolean getIpv4AllocationRaceCheck() { + return this.ipv4AllocationRaceCheck; + } + + public void setIpv4AllocationRaceCheck(boolean ipv4AllocationRaceCheck) { + this.ipv4AllocationRaceCheck = ipv4AllocationRaceCheck; + } // // OTHER METHODS @@ -442,6 +450,9 @@ public void deallocate() { @Override public String toString() { - return String.format("NicProfile %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "vmId", "reservationId", "iPv4Address", "broadcastUri")); + return String.format("NicProfile %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "vmId", "deviceId", + "broadcastUri", "reservationId", "iPv4Address")); } } diff --git a/api/src/main/java/com/cloud/vm/UserVmService.java b/api/src/main/java/com/cloud/vm/UserVmService.java index c32c099ed3a2..01f11b73cd41 100644 --- a/api/src/main/java/com/cloud/vm/UserVmService.java +++ b/api/src/main/java/com/cloud/vm/UserVmService.java @@ -16,14 +16,18 @@ // under the License. package com.cloud.vm; +import com.cloud.storage.Snapshot; +import com.cloud.storage.Volume; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import com.cloud.deploy.DeploymentPlan; import org.apache.cloudstack.api.BaseCmd.HTTPMethod; import org.apache.cloudstack.api.command.admin.vm.AssignVMCmd; import org.apache.cloudstack.api.command.admin.vm.RecoverVMCmd; import org.apache.cloudstack.api.command.user.vm.AddNicToVMCmd; +import org.apache.cloudstack.api.command.user.vm.CreateVMFromBackupCmd; import org.apache.cloudstack.api.command.user.vm.DeployVMCmd; import org.apache.cloudstack.api.command.user.vm.DestroyVMCmd; import org.apache.cloudstack.api.command.user.vm.RebootVMCmd; @@ -42,9 +46,11 @@ import org.apache.cloudstack.api.command.user.vmgroup.DeleteVMGroupCmd; import com.cloud.dc.DataCenter; +import com.cloud.deploy.DeploymentPlanner; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.ManagementServerException; +import com.cloud.exception.OperationTimedoutException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.exception.StorageUnavailableException; @@ -58,6 +64,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 { @@ -66,10 +73,7 @@ public interface UserVmService { /** * Destroys one virtual machine * - * @param userId - * the id of the user performing the action - * @param vmId - * the id of the virtual machine. + * @param cmd the API Command Object containg the parameters to use for this service action * @throws ConcurrentOperationException * @throws ResourceUnavailableException */ @@ -110,7 +114,13 @@ public interface UserVmService { UserVm startVirtualMachine(StartVMCmd cmd) throws StorageUnavailableException, ExecutionException, ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException; - UserVm rebootVirtualMachine(RebootVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException; + UserVm rebootVirtualMachine(RebootVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException, ResourceAllocationException; + + void startVirtualMachine(UserVm vm, DeploymentPlan plan) throws OperationTimedoutException, ResourceUnavailableException, InsufficientCapacityException; + + void startVirtualMachineForHA(VirtualMachine vm, Map params, + DeploymentPlanner planner) throws InsufficientCapacityException, ResourceUnavailableException, + ConcurrentOperationException, OperationTimedoutException; UserVm updateVirtualMachine(UpdateVMCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException; @@ -148,14 +158,6 @@ UserVm startVirtualMachine(StartVMCmd cmd) throws StorageUnavailableException, E * Creates a Basic Zone User VM in the database and returns the VM to the * caller. * - * - * - * @param sshKeyPair - * - name of the ssh key pair used to login to the virtual - * machine - * @param cpuSpeed - * @param memory - * @param cpuNumber * @param zone * - availability zone for the virtual machine * @param serviceOffering @@ -220,20 +222,17 @@ UserVm startVirtualMachine(StartVMCmd cmd) throws StorageUnavailableException, E * available. */ UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List securityGroupIdList, - Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, + Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, List dataDiskInfoList, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData, Long userDataId, String userDataDetails, List sshKeyPairs, Map requestedIps, IpAddresses defaultIp, Boolean displayVm, String keyboard, List affinityGroupIdList, Map customParameter, String customId, Map> dhcpOptionMap, Map dataDiskTemplateToDiskOfferingMap, - Map userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId) throws InsufficientCapacityException, + Map userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId, Volume volume, Snapshot snapshot) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; /** * Creates a User VM in Advanced Zone (Security Group feature is enabled) in * the database and returns the VM to the caller. * - * - * - * @param type * @param zone * - availability zone for the virtual machine * @param serviceOffering @@ -300,23 +299,15 @@ UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering s * available. */ UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List networkIdList, - List securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, + List securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, List dataDiskInfoList, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData, Long userDataId, String userDataDetails, List sshKeyPairs, Map requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, List affinityGroupIdList, Map customParameters, String customId, Map> dhcpOptionMap, - Map dataDiskTemplateToDiskOfferingMap, Map userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId, String vmType) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; + Map dataDiskTemplateToDiskOfferingMap, Map userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId, String vmType, Volume volume, Snapshot snapshot) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; /** * Creates a User VM in Advanced Zone (Security Group feature is disabled) * in the database and returns the VM to the caller. * - * - * - * @param sshKeyPair - * - name of the ssh key pair used to login to the virtual - * machine - * @param cpuSpeed - * @param memory - * @param cpuNumber * @param zone * - availability zone for the virtual machine * @param serviceOffering @@ -380,10 +371,10 @@ UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, ServiceOfferin * available. */ UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List networkIdList, Account owner, - String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData, + String hostName, String displayName, Long diskOfferingId, Long diskSize, List dataDiskInfoList, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData, Long userDataId, String userDataDetails, List sshKeyPairs, Map requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, List affinityGroupIdList, Map customParameters, String customId, Map> dhcpOptionMap, Map dataDiskTemplateToDiskOfferingMap, - Map templateOvfPropertiesMap, boolean dynamicScalingEnabled, String vmType, Long overrideDiskOfferingId) + Map templateOvfPropertiesMap, boolean dynamicScalingEnabled, String vmType, Long overrideDiskOfferingId, Volume volume, Snapshot snapshot) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; @@ -483,16 +474,16 @@ VirtualMachine migrateVirtualMachine(Long vmId, Host destinationHost) throws Res VirtualMachine migrateVirtualMachineWithVolume(Long vmId, Host destinationHost, Map volumeToPool) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException; - UserVm moveVMToUser(AssignVMCmd moveUserVMCmd) throws ResourceAllocationException, ConcurrentOperationException, ResourceUnavailableException, + UserVm moveVmToUser(AssignVMCmd moveUserVMCmd) throws ResourceAllocationException, ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException; VirtualMachine vmStorageMigration(Long vmId, StoragePool destPool); VirtualMachine vmStorageMigration(Long vmId, Map volumeToPool); - UserVm restoreVM(RestoreVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException; + UserVm restoreVM(RestoreVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException, ResourceAllocationException; - UserVm restoreVirtualMachine(Account caller, long vmId, Long newTemplateId) throws InsufficientCapacityException, ResourceUnavailableException; + UserVm restoreVirtualMachine(Account caller, long vmId, Long newTemplateId, Long rootDiskOfferingId, boolean expunge, Map details) throws InsufficientCapacityException, ResourceUnavailableException; UserVm upgradeVirtualMachine(ScaleVMCmd cmd) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException; @@ -517,14 +508,43 @@ 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, + /** + * 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 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 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; + + UserVm restoreVMFromBackup(CreateVMFromBackupCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException; } diff --git a/api/src/main/java/com/cloud/vm/VirtualMachine.java b/api/src/main/java/com/cloud/vm/VirtualMachine.java index e7c5efb773b1..d244de7115e8 100644 --- a/api/src/main/java/com/cloud/vm/VirtualMachine.java +++ b/api/src/main/java/com/cloud/vm/VirtualMachine.java @@ -128,7 +128,6 @@ public static StateMachine2 getStat s_fsm.addTransition(new Transition(State.Error, VirtualMachine.Event.DestroyRequested, State.Expunging, null)); s_fsm.addTransition(new Transition(State.Error, VirtualMachine.Event.ExpungeOperation, State.Expunging, null)); s_fsm.addTransition(new Transition(State.Stopped, Event.RestoringRequested, State.Restoring, null)); - s_fsm.addTransition(new Transition(State.Expunging, Event.RestoringRequested, State.Restoring, null)); s_fsm.addTransition(new Transition(State.Destroyed, Event.RestoringRequested, State.Restoring, null)); s_fsm.addTransition(new Transition(State.Restoring, Event.RestoringSuccess, State.Stopped, null)); s_fsm.addTransition(new Transition(State.Restoring, Event.RestoringFailed, State.Stopped, null)); @@ -333,6 +332,8 @@ public boolean isUsedBySystem() { */ Date getCreated(); + Date getRemoved(); + long getServiceOfferingId(); Long getBackupOfferingId(); diff --git a/api/src/main/java/com/cloud/vm/VirtualMachineProfile.java b/api/src/main/java/com/cloud/vm/VirtualMachineProfile.java index 62519412fdb5..5c78d6bedd64 100644 --- a/api/src/main/java/com/cloud/vm/VirtualMachineProfile.java +++ b/api/src/main/java/com/cloud/vm/VirtualMachineProfile.java @@ -60,6 +60,8 @@ public interface VirtualMachineProfile { void setConfigDriveLocation(NetworkElement.Location location); + void setServiceOffering(ServiceOffering offering); + public static class Param { public static final Param VmPassword = new Param("VmPassword"); @@ -76,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; @@ -190,6 +193,10 @@ public boolean equals(Object obj) { Map getParameters(); + void setCpuOvercommitRatio(Float cpuOvercommitRatio); + + void setMemoryOvercommitRatio(Float memoryOvercommitRatio); + Float getCpuOvercommitRatio(); Float getMemoryOvercommitRatio(); diff --git a/api/src/main/java/com/cloud/vm/VmDetailConstants.java b/api/src/main/java/com/cloud/vm/VmDetailConstants.java index 9338cc11cd4d..9e56bf4f17b2 100644 --- a/api/src/main/java/com/cloud/vm/VmDetailConstants.java +++ b/api/src/main/java/com/cloud/vm/VmDetailConstants.java @@ -19,6 +19,7 @@ public interface VmDetailConstants { String KEYBOARD = "keyboard"; String CPU_CORE_PER_SOCKET = "cpu.corespersocket"; + String CPU_THREAD_PER_CORE = "cpu.threadspercore"; String ROOT_DISK_SIZE = "rootdisksize"; String BOOT_MODE = "boot.mode"; String NAME_ON_HYPERVISOR= "nameonhypervisor"; @@ -40,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"; @@ -53,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"; @@ -73,6 +78,7 @@ public interface VmDetailConstants { String ENCRYPTED_PASSWORD = "Encrypted.Password"; String CONFIG_DRIVE_LOCATION = "configDriveLocation"; + String LAST_CONFIG_DRIVE_LOCATION = "lastConfigDriveLocation"; String SKIP_DRS = "skipFromDRS"; @@ -87,6 +93,10 @@ public interface VmDetailConstants { String DEPLOY_AS_IS_CONFIGURATION = "configurationId"; String KEY_PAIR_NAMES = "keypairnames"; String CKS_CONTROL_NODE_LOGIN_USER = "controlNodeLoginUser"; + String CKS_NODE_TYPE = "node"; + String OFFERING = "offering"; + String TEMPLATE = "template"; + String AFFINITY_GROUP = "affinitygroup"; // VMware to KVM VM migrations specific String VMWARE_TO_KVM_PREFIX = "vmware-to-kvm"; @@ -99,4 +109,25 @@ public interface VmDetailConstants { String VMWARE_HOST_NAME = String.format("%s-host", VMWARE_TO_KVM_PREFIX); String VMWARE_DISK = String.format("%s-disk", VMWARE_TO_KVM_PREFIX); String VMWARE_MAC_ADDRESSES = String.format("%s-mac-addresses", VMWARE_TO_KVM_PREFIX); + + // TPM + String VIRTUAL_TPM_ENABLED = "virtual.tpm.enabled"; + String VIRTUAL_TPM_MODEL = "virtual.tpm.model"; + String VIRTUAL_TPM_VERSION = "virtual.tpm.version"; + + // CPU mode and model, ADMIN only + String GUEST_CPU_MODE = "guest.cpu.mode"; + String GUEST_CPU_MODEL = "guest.cpu.model"; + + // Lease related + String INSTANCE_LEASE_EXPIRY_DATE = "leaseexpirydate"; + String INSTANCE_LEASE_EXPIRY_ACTION = "leaseexpiryaction"; + String INSTANCE_LEASE_EXECUTION = "leaseactionexecution"; + + // External orchestrator related + String MAC_ADDRESS = "mac_address"; + String EXPUNGE_EXTERNAL_VM = "expunge.external.vm"; + String EXTERNAL_DETAIL_PREFIX = "External:"; + String CLOUDSTACK_VM_DETAILS = "cloudstack.vm.details"; + String CLOUDSTACK_VLAN = "cloudstack.vlan"; } diff --git a/api/src/main/java/com/cloud/vm/VmDiskInfo.java b/api/src/main/java/com/cloud/vm/VmDiskInfo.java new file mode 100644 index 000000000000..b8779a8d77c6 --- /dev/null +++ b/api/src/main/java/com/cloud/vm/VmDiskInfo.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.cloud.vm; + +import com.cloud.offering.DiskOffering; +import com.cloud.offering.DiskOfferingInfo; + +public class VmDiskInfo extends DiskOfferingInfo { + private Long _deviceId; + + public VmDiskInfo(DiskOffering diskOffering, Long size, Long minIops, Long maxIops) { + super(diskOffering, size, minIops, maxIops); + } + + public VmDiskInfo(DiskOffering diskOffering, Long size, Long minIops, Long maxIops, Long deviceId) { + super(diskOffering, size, minIops, maxIops); + _deviceId = deviceId; + } + + public Long getDeviceId() { + return _deviceId; + } +} diff --git a/api/src/main/java/com/cloud/vm/snapshot/VMSnapshot.java b/api/src/main/java/com/cloud/vm/snapshot/VMSnapshot.java index 3897df2d5e67..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,9 +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( - "The volume is destroyed, and can't be recovered."), Error("The volume is in error state, and can't be recovered"); + 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 Instance snapshot is hidden from the user and cannot be recovered."); String _description; @@ -60,6 +61,8 @@ public String getDescription() { s_fsm.addTransition(Expunging, Event.ExpungeRequested, Expunging); s_fsm.addTransition(Expunging, Event.OperationSucceeded, Removed); s_fsm.addTransition(Expunging, Event.OperationFailed, Error); + s_fsm.addTransition(Expunging, Event.Hide, Hidden); + s_fsm.addTransition(Hidden, Event.ExpungeRequested, Expunging); } } @@ -68,7 +71,7 @@ enum Type { } enum Event { - CreateRequested, OperationFailed, OperationSucceeded, RevertRequested, ExpungeRequested, + CreateRequested, OperationFailed, OperationSucceeded, RevertRequested, ExpungeRequested, Hide, } @Override 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/Role.java b/api/src/main/java/org/apache/cloudstack/acl/Role.java index 5e5ffd583d87..ce4166ef4c81 100644 --- a/api/src/main/java/org/apache/cloudstack/acl/Role.java +++ b/api/src/main/java/org/apache/cloudstack/acl/Role.java @@ -21,7 +21,18 @@ import org.apache.cloudstack.api.InternalIdentity; public interface Role extends RoleEntity, InternalIdentity, Identity { + + enum State { + ENABLED, DISABLED; + + @Override + public String toString(){ + return super.toString().toLowerCase(); + } + } + RoleType getRoleType(); boolean isDefault(); boolean isPublicRole(); + State getState(); } 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 9becce643d40..14e0a608a925 100644 --- a/api/src/main/java/org/apache/cloudstack/acl/RoleService.java +++ b/api/src/main/java/org/apache/cloudstack/acl/RoleService.java @@ -30,6 +30,11 @@ public interface RoleService { ConfigKey EnableDynamicApiChecker = new ConfigKey<>("Advanced", Boolean.class, "dynamic.apichecker.enabled", "false", "If set to true, this enables the dynamic role-based api access checker and disables the default static role-based api access checker.", true); + ConfigKey DynamicApiCheckerCachePeriod = new ConfigKey<>("Advanced", Integer.class, + "dynamic.apichecker.cache.period", "0", + "Defines the expiration time in seconds for the Dynamic API Checker cache, determining how long cached data is retained before being refreshed. If set to zero then caching will be disabled", + false); + boolean isEnabled(); /** @@ -38,7 +43,9 @@ public interface RoleService { * Moreover, we will check if the requested role is of 'Admin' type; roles with 'Admin' type should only be visible to 'root admins'. * Therefore, if a non-'root admin' user tries to search for an 'Admin' role, this method will return null. */ - Role findRole(Long id, boolean removePrivateRoles); + Role findRole(Long id, boolean ignorePrivateRoles); + + List findRoles(List ids, boolean ignorePrivateRoles); Role findRole(Long id); @@ -52,6 +59,10 @@ public interface RoleService { boolean deleteRole(Role role); + boolean enableRole(Role role); + + boolean disableRole(Role role); + RolePermission findRolePermission(Long id); RolePermission findRolePermissionByRoleIdAndRule(Long roleId, String rule); @@ -74,7 +85,7 @@ public interface RoleService { */ List listRoles(); - Pair, Integer> listRoles(Long startIndex, Long limit); + Pair, Integer> listRoles(String state, Long startIndex, Long limit); /** * Find all roles that have the giving {@link String} as part of their name. @@ -82,16 +93,37 @@ public interface RoleService { */ List findRolesByName(String name); - Pair, Integer> findRolesByName(String name, String keyword, Long startIndex, Long limit); + Pair, Integer> findRolesByName(String name, String keyword, String state, Long startIndex, Long limit); /** * Find all roles by {@link RoleType}. If the role type is {@link RoleType#Admin}, the calling account must be a root admin, otherwise we return an empty list. */ List findRolesByType(RoleType roleType); - Pair, Integer> findRolesByType(RoleType roleType, Long startIndex, Long limit); + Pair, Integer> findRolesByType(RoleType roleType, String state, Long startIndex, Long limit); 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 005d47c85bc2..c33488cd9239 100644 --- a/api/src/main/java/org/apache/cloudstack/acl/RoleType.java +++ b/api/src/main/java/org/apache/cloudstack/acl/RoleType.java @@ -23,8 +23,11 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; +import java.util.Collection; +import java.util.EnumSet; import java.util.HashMap; import java.util.Map; +import java.util.Set; // Enum for default roles in CloudStack public enum RoleType { @@ -100,15 +103,39 @@ public static Long getRoleByAccountType(final Long roleId, final Account.Type ac return roleId; } + public static int toCombinedMask(Collection roles) { + int combinedMask = 0; + if (roles != null) { + for (RoleType role : roles) { + combinedMask |= role.getMask(); + } + } + return combinedMask; + } + + public static Set fromCombinedMask(int combinedMask) { + Set roles = EnumSet.noneOf(RoleType.class); + for (RoleType roleType : RoleType.values()) { + if ((combinedMask & roleType.getMask()) != 0) { + roles.add(roleType); + } + } + if (roles.isEmpty()) { + roles.add(Unknown); + } + return roles; + } + + /** * This method returns the role account type if the role isn't null, else it returns the default account type. * */ 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 22842b834feb..f5a71b994525 100644 --- a/api/src/main/java/org/apache/cloudstack/affinity/AffinityGroupResponse.java +++ b/api/src/main/java/org/apache/cloudstack/affinity/AffinityGroupResponse.java @@ -25,6 +25,7 @@ import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.EntityReference; import org.apache.cloudstack.api.response.ControlledViewEntityResponse; +import org.apache.cloudstack.dedicated.DedicatedResourceResponse; import com.cloud.serializer.Param; @@ -33,45 +34,53 @@ 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) + @Param(description = "path of the Domain the affinity group belongs to", since = "4.19.2.0") + 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") + @Param(description = "dedicated resources associated with this affinity group") + private List dedicatedResources; + public AffinityGroupResponse() { } @@ -115,6 +124,11 @@ public void setDomainName(String domainName) { this.domainName = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } + @Override public int hashCode() { final int prime = 31; @@ -162,4 +176,12 @@ public void addVMId(String vmId) { this.vmIdList.add(vmId); } + public void addDedicatedResource(DedicatedResourceResponse dedicatedResourceResponse) { + if (this.dedicatedResources == null) { + this.dedicatedResources = new ArrayList<>(); + } + + this.dedicatedResources.add(dedicatedResourceResponse); + } + } 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 50e48526d26c..fcc87908bd5d 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,34 +51,38 @@ 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_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 short getType() { return type; @@ -82,6 +92,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) { @@ -91,6 +105,10 @@ private static AlertType getAlertType(short type) { return null; } + public static Set getAlertTypes() { + return defaultAlertTypes; + } + @Override public String toString() { return String.valueOf(this.getType()); @@ -101,7 +119,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/ApiArgValidator.java b/api/src/main/java/org/apache/cloudstack/api/ApiArgValidator.java index 3e06fc0e44eb..380472352735 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiArgValidator.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiArgValidator.java @@ -32,4 +32,9 @@ public enum ApiArgValidator { * Validates if the parameter is an UUID with the method {@link UuidUtils#isUuid(String)}. */ UuidString, + + /** + * Validates if the parameter is a valid RFC Compliance domain name. + */ + RFCComplianceDomainName, } 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 affceb4e3f95..4d33ba859a5b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiCommandResourceType.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiCommandResourceType.java @@ -17,7 +17,9 @@ package org.apache.cloudstack.api; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.apache.cloudstack.region.PortableIp; import org.apache.commons.collections.CollectionUtils; @@ -59,6 +61,7 @@ public enum ApiCommandResourceType { AffinityGroup(org.apache.cloudstack.affinity.AffinityGroup.class), InternalLbVm(com.cloud.network.router.VirtualRouter.class), DedicatedGuestVlanRange(com.cloud.network.GuestVlan.class), + GuestOsCategory(com.cloud.storage.GuestOsCategory.class), GuestOs(com.cloud.storage.GuestOS.class), GuestOsMapping(com.cloud.storage.GuestOSHypervisor.class), Network(com.cloud.network.Network.class), @@ -80,15 +83,26 @@ public enum ApiCommandResourceType { VpnCustomerGateway(com.cloud.network.Site2SiteCustomerGateway.class), ManagementServer(org.apache.cloudstack.management.ManagementServerHost.class), ObjectStore(org.apache.cloudstack.storage.object.ObjectStore.class), - Bucket(org.apache.cloudstack.storage.object.Bucket.class); + Bucket(org.apache.cloudstack.storage.object.Bucket.class), + QuotaTariff(org.apache.cloudstack.quota.QuotaTariff.class), + KubernetesCluster(com.cloud.kubernetes.cluster.KubernetesCluster.class), + KubernetesSupportedVersion(null), + SharedFS(org.apache.cloudstack.storage.sharedfs.SharedFS.class), + Extension(org.apache.cloudstack.extension.Extension.class), + ExtensionCustomAction(org.apache.cloudstack.extension.ExtensionCustomAction.class); private final Class clazz; + static final Map> additionalClassMappings = new HashMap<>(); + private ApiCommandResourceType(Class clazz) { this.clazz = clazz; } public Class getAssociatedClass() { + if (this.clazz == null && additionalClassMappings.containsKey(this)) { + return additionalClassMappings.get(this); + } return this.clazz; } @@ -118,4 +132,8 @@ public static ApiCommandResourceType fromString(String value) { } return null; } + + public static void setClassMapping(ApiCommandResourceType type, Class clazz) { + additionalClassMappings.put(type, clazz); + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index db0c5ce494c1..be4087a39ece 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -19,20 +19,35 @@ 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_TYPE = "accounttype"; public static final String ACCOUNT_ID = "accountid"; public static final String ACCOUNT_IDS = "accountids"; public static final String ACCUMULATE = "accumulate"; + public static final String ACQUIRED = "acquired"; public static final String ACTIVATION_RULE = "activationrule"; public static final String ACTIVITY = "activity"; public static final String ADAPTER_TYPE = "adaptertype"; + public static final String ADDITONAL_CONFIG_ENABLED = "additionalconfigenabled"; public static final String ADDRESS = "address"; public static final String ALGORITHM = "algorithm"; public static final String ALIAS = "alias"; + public static final String ALLOCATED = "allocated"; + public static final String ALLOCATED_DATE = "allocateddate"; public static final String ALLOCATED_ONLY = "allocatedonly"; + public static final String ALLOCATED_TIME = "allocated"; + public static final String ALLOWED_ROLE_TYPES = "allowedroletypes"; + public static final String ALLOW_USER_FORCE_STOP_VM = "allowuserforcestopvm"; public static final String ANNOTATION = "annotation"; public static final String API_KEY = "apikey"; + public static final String API_KEY_ACCESS = "apikeyaccess"; public static final String ARCHIVED = "archived"; + public static final String ARCH = "arch"; + public static final String AS_NUMBER = "asnumber"; + 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"; @@ -42,14 +57,26 @@ public class ApiConstants { public static final String AVAILABLE = "available"; public static final String AVAILABLE_SUBNETS = "availablesubnets"; public static final String AVAILABLE_VIRTUAL_MACHINE_COUNT = "availablevirtualmachinecount"; + public static final String BACKUP_AVAILABLE = "backupavailable"; public static final String BACKUP_ID = "backupid"; + 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_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 BASE64_IMAGE = "base64image"; + public static final String BGP_PEERS = "bgppeers"; + public static final String BGP_PEER_IDS = "bgppeerids"; + public static final String BATCH_SIZE = "batchsize"; public static final String BITS = "bits"; public static final String BOOTABLE = "bootable"; public static final String BIND_DN = "binddn"; public static final String BIND_PASSWORD = "bindpass"; + public static final String BUS_ADDRESS = "busaddress"; public static final String BYTES_READ_RATE = "bytesreadrate"; public static final String BYTES_READ_RATE_MAX = "bytesreadratemax"; public static final String BYTES_READ_RATE_MAX_LENGTH = "bytesreadratemaxlength"; @@ -57,6 +84,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"; @@ -72,9 +100,13 @@ public class ApiConstants { public static final String CONVERT_INSTANCE_HOST_ID = "convertinstancehostid"; public static final String CONVERT_INSTANCE_STORAGE_POOL_ID = "convertinstancepoolid"; public static final String ENABLED_REVOCATION_CHECK = "enabledrevocationcheck"; + public static final String CLIENT_ADDRESS = "clientaddress"; + public static final String COMBINED_CAPACITY_ORDERING = "COMBINED"; public static final String CONTROLLER = "controller"; public static final String CONTROLLER_UNIT = "controllerunit"; + public static final String CONSOLE_ENDPOINT_CREATOR_ADDRESS = "consoleendpointcreatoraddress"; public static final String COPY_IMAGE_TAGS = "copyimagetags"; + public static final String CPU_OVERCOMMIT_RATIO = "cpuOvercommitRatio"; public static final String CSR = "csr"; public static final String PRIVATE_KEY = "privatekey"; public static final String DATASTORE_HOST = "datastorehost"; @@ -87,6 +119,8 @@ public class ApiConstants { public static final String DNS_SEARCH_ORDER = "dnssearchorder"; public static final String CHAIN_INFO = "chaininfo"; public static final String CIDR = "cidr"; + public static final String CIDR_SIZE = "cidrsize"; + public static final String IP6_CIDR = "ip6cidr"; public static final String CIDR_LIST = "cidrlist"; public static final String DEST_CIDR_LIST = "destcidrlist"; @@ -99,12 +133,19 @@ public class ApiConstants { public static final String CN = "cn"; public static final String COMMAND = "command"; public static final String CMD_EVENT_TYPE = "cmdeventtype"; + public static final String CNI_CONFIG = "cniconfig"; + public static final String CNI_CONFIG_ID = "cniconfigurationid"; + public static final String CNI_CONFIG_DETAILS = "cniconfigdetails"; + public static final String CNI_CONFIG_NAME = "cniconfigname"; + public static final String CSI_ENABLED = "csienabled"; public static final String COMPONENT = "component"; + public static final String CPU = "CPU"; public static final String CPU_CORE_PER_SOCKET = "cpucorepersocket"; public static final String CPU_NUMBER = "cpunumber"; public static final String CPU_SPEED = "cpuspeed"; 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"; @@ -113,27 +154,35 @@ public class ApiConstants { public static final String CUSTOMIZED = "customized"; public static final String CUSTOMIZED_IOPS = "customizediops"; public static final String CUSTOM_ID = "customid"; + public static final String CUSTOM_ACTION_ID = "customactionid"; public static final String CUSTOM_JOB_ID = "customjobid"; public static final String CURRENT_START_IP = "currentstartip"; public static final String CURRENT_END_IP = "currentendip"; public static final String ENCRYPT = "encrypt"; + public static final String ENCRYPT_FORMAT = "encryptformat"; public static final String ENCRYPT_ROOT = "encryptroot"; public static final String ENCRYPTION_SUPPORTED = "encryptionsupported"; + public static final String ETCD_IPS = "etcdips"; public static final String MIN_IOPS = "miniops"; public static final String MAX_IOPS = "maxiops"; public static final String HYPERVISOR_SNAPSHOT_RESERVE = "hypervisorsnapshotreserve"; public static final String DATACENTER_NAME = "datacentername"; + public static final String DATADISKS_DETAILS = "datadisksdetails"; public static final String DATADISK_OFFERING_LIST = "datadiskofferinglist"; public static final String DEFAULT_VALUE = "defaultvalue"; + public static final String DELETE_PROTECTION = "deleteprotection"; public static final String DESCRIPTION = "description"; public static final String DESTINATION = "destination"; public static final String DESTINATION_ZONE_ID = "destzoneid"; public static final String DETAILS = "details"; public static final String DEVICE_ID = "deviceid"; + public static final String DEVICE_IDS = "deviceids"; + public static final String DEVICE_NAME = "devicename"; public static final String DIRECT_DOWNLOAD = "directdownload"; public static final String DISK = "disk"; public static final String DISK_OFFERING_ID = "diskofferingid"; public static final String NEW_DISK_OFFERING_ID = "newdiskofferingid"; + public static final String ORCHESTRATOR_REQUIRES_PREPARE_VM = "orchestratorrequirespreparevm"; public static final String OVERRIDE_DISK_OFFERING_ID = "overridediskofferingid"; public static final String DISK_KBS_READ = "diskkbsread"; public static final String DISK_KBS_WRITE = "diskkbswrite"; @@ -169,28 +218,47 @@ public class ApiConstants { 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"; public static final String END_IPV6 = "endipv6"; public static final String END_PORT = "endport"; public static final String ENTRY_TIME = "entrytime"; + public static final String ERROR_MESSAGE = "errormessage"; + public static final String EVENT_ID = "eventid"; + public static final String EVENT_TYPE = "eventtype"; public static final String EXPIRES = "expires"; 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"; public static final String EXTRA_DHCP_OPTION_VALUE = "extradhcpvalue"; public static final String EXTERNAL = "external"; public static final String EXTERNAL_UUID = "externaluuid"; + public static final String EXTERNAL_DETAILS = "externaldetails"; + public static final String PARAMETERS = "parameters"; + public static final String EXTENSION = "extension"; + public static final String EXTENSION_ID = "extensionid"; + public static final String EXTENSION_NAME = "extensionname"; + public static final String EXTENSIONS_PATH = "extensionspath"; public static final String FENCE = "fence"; public static final String FETCH_LATEST = "fetchlatest"; + public static final String FILESYSTEM = "filesystem"; 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"; public static final String FORMAT = "format"; public static final String FOR_VIRTUAL_NETWORK = "forvirtualnetwork"; public static final String FOR_SYSTEM_VMS = "forsystemvms"; + public static final String FOR_PROVIDER = "forprovider"; + public static final String FULL_PATH = "fullpath"; public static final String GATEWAY = "gateway"; public static final String IP6_GATEWAY = "ip6gateway"; public static final String GROUP = "group"; @@ -207,6 +275,7 @@ public class ApiConstants { public static final String HA_PROVIDER = "haprovider"; public static final String HA_STATE = "hastate"; 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 HOST_ID = "hostid"; public static final String HOST_IDS = "hostids"; @@ -215,6 +284,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"; @@ -222,6 +292,7 @@ public class ApiConstants { public static final String ICMP_TYPE = "icmptype"; public static final String ID = "id"; public static final String IDS = "ids"; + public static final String IMPORT_INSTANCE_HOST_ID = "importinstancehostid"; public static final String INDEX = "index"; public static final String INSTANCES_DISKS_STATS_RETENTION_ENABLED = "instancesdisksstatsretentionenabled"; public static final String INSTANCES_DISKS_STATS_RETENTION_TIME = "instancesdisksstatsretentiontime"; @@ -232,13 +303,18 @@ public class ApiConstants { public static final String PREVIOUS_OWNER_ID = "previousownerid"; public static final String PREVIOUS_OWNER_NAME = "previousownername"; public static final String NEXT_ACL_RULE_ID = "nextaclruleid"; + public static final String NEXT_HOP = "nexthop"; public static final String MOVE_ACL_CONSISTENCY_HASH = "aclconsistencyhash"; public static final String IMAGE_PATH = "imagepath"; + public static final String INSTANCE_CONVERSION_SUPPORTED = "instanceconversionsupported"; public static final String INTERNAL_DNS1 = "internaldns1"; public static final String INTERNAL_DNS2 = "internaldns2"; public static final String INTERNET_PROTOCOL = "internetprotocol"; public static final String INTERVAL_TYPE = "intervaltype"; - public static final String LOCATION_TYPE = "locationtype"; + public static final String INSTANCE_LEASE_DURATION = "leaseduration"; + public static final String INSTANCE_LEASE_ENABLED = "instanceleaseenabled"; + public static final String INSTANCE_LEASE_EXPIRY_ACTION = "leaseexpiryaction"; + public static final String INSTANCE_LEASE_EXPIRY_DATE= "leaseexpirydate"; public static final String IOPS_READ_RATE = "iopsreadrate"; public static final String IOPS_READ_RATE_MAX = "iopsreadratemax"; public static final String IOPS_READ_RATE_MAX_LENGTH = "iopsreadratemaxlength"; @@ -248,6 +324,7 @@ public class ApiConstants { public static final String IP_ADDRESS = "ipaddress"; public static final String IP_ADDRESSES = "ipaddresses"; public static final String IP6_ADDRESS = "ip6address"; + public static final String IP6_ADDRESSES = "ip6addresses"; public static final String IP_ADDRESS_ID = "ipaddressid"; public static final String IS_2FA_ENABLED = "is2faenabled"; public static final String IS_2FA_VERIFIED = "is2faverified"; @@ -261,8 +338,11 @@ public class ApiConstants { public static final String IS_CLEANUP_REQUIRED = "iscleanuprequired"; public static final String IS_DYNAMIC = "isdynamic"; public static final String IS_EDGE = "isedge"; + public static final String IS_ENCRYPTED = "isencrypted"; public static final String IS_EXTRACTABLE = "isextractable"; public static final String IS_FEATURED = "isfeatured"; + public static final String IS_IMPLICIT = "isimplicit"; + public static final String IS_ISO = "isiso"; public static final String IS_PORTABLE = "isportable"; public static final String IS_PUBLIC = "ispublic"; public static final String IS_PERSISTENT = "ispersistent"; @@ -278,42 +358,63 @@ 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"; public static final String LAST_BOOT = "lastboottime"; public static final String LAST_SERVER_START = "lastserverstart"; public static final String LAST_SERVER_STOP = "lastserverstop"; + public static final String LEASED = "leased"; public static final String LEVEL = "level"; public static final String LENGTH = "length"; + public static final String LIMIT = "limit"; public static final String LIMIT_CPU_USE = "limitcpuuse"; public static final String LIST_HOSTS = "listhosts"; + public static final String LOCATION_TYPE = "locationtype"; public static final String LOCK = "lock"; public static final String LUN = "lun"; public static final String LBID = "lbruleid"; public static final String LB_PROVIDER = "lbprovider"; public static final String MAC_ADDRESS = "macaddress"; + public static final String MAC_ADDRESSES = "macaddresses"; + public static final String MANUAL_UPGRADE = "manualupgrade"; + public static final String MATCH_TYPE = "matchtype"; public static final String MAX = "max"; public static final String MAX_SNAPS = "maxsnaps"; + public static final String MAX_BACKUPS = "maxbackups"; public static final String MAX_CPU_NUMBER = "maxcpunumber"; public static final String MAX_MEMORY = "maxmemory"; + public static final String MEMORY_OVERCOMMIT_RATIO = "memoryOvercommitRatio"; + public static final String MESSAGE = "message"; public static final String MIN_CPU_NUMBER = "mincpunumber"; public static final String MIN_MEMORY = "minmemory"; public static final String MIGRATION_TYPE = "migrationtype"; + public static final String MIGRATION_JOB_ID = "migrationjobid"; + public static final String MIGRATION_JOB_STATUS = "migrationjobstatus"; public static final String MIGRATIONS = "migrations"; public static final String MEMORY = "memory"; public static final String MODE = "mode"; + public static final String MOUNT_CKS_ISO_ON_VR = "mountcksisoonvr"; + public static final String MULTI_ARCH = "ismultiarch"; + public static final String NSX_MODE = "nsxmode"; + public static final String NETWORK_MODE = "networkmode"; + public static final String NSX_ENABLED = "isnsxenabled"; public static final String NAME = "name"; public static final String METHOD_NAME = "methodname"; public static final String NETWORK_DOMAIN = "networkdomain"; public static final String NETMASK = "netmask"; public static final String NEW_NAME = "newname"; public static final String NIC = "nic"; + public static final String NICS = "nics"; public static final String NIC_NETWORK_LIST = "nicnetworklist"; public static final String NIC_IP_ADDRESS_LIST = "nicipaddresslist"; public static final String NIC_MULTIQUEUE_NUMBER = "nicmultiqueuenumber"; public static final String NIC_PACKED_VIRTQUEUES_ENABLED = "nicpackedvirtqueuesenabled"; public static final String NEW_START_IP = "newstartip"; public static final String NEW_END_IP = "newendip"; + public static final String KUBERNETES_NODE_VERSION = "kubernetesnodeversion"; + public static final String NUMA_NODE = "numanode"; public static final String NUM_RETRIES = "numretries"; public static final String OFFER_HA = "offerha"; public static final String OS_DISTRIBUTION = "osdistribution"; @@ -330,6 +431,13 @@ public class ApiConstants { public static final String OS_TYPE_ID = "ostypeid"; public static final String OS_DISPLAY_NAME = "osdisplayname"; public static final String OS_NAME_FOR_HYPERVISOR = "osnameforhypervisor"; + public static final String GPU_CARD_ID = "gpucardid"; + public static final String GPU_CARD_NAME = "gpucardname"; + public static final String GPU_COUNT = "gpucount"; + public static final String GPU_DISPLAY = "gpudisplay"; + public static final String GPU_DEVICE_TYPE = "gpudevicetype"; + public static final String GPU_ENABLED = "gpuenabled"; + public static final String MAX_VGPU_PER_PHYSICAL_GPU = "maxvgpuperphysicalgpu"; public static final String GUEST_OS_LIST = "guestoslist"; public static final String GUEST_OS_COUNT = "guestoscount"; public static final String OS_MAPPING_CHECK_ENABLED = "osmappingcheckenabled"; @@ -341,14 +449,27 @@ public class ApiConstants { public static final String PARENT = "parent"; public static final String PARENT_ID = "parentid"; public static final String PARENT_DOMAIN_ID = "parentdomainid"; + public static final String PARENT_GPU_DEVICE_ID = "parentgpudeviceid"; + public static final String PARENT_SUBNET = "parentsubnet"; public static final String PARENT_TEMPLATE_ID = "parenttemplateid"; public static final String PASSWORD = "password"; + public static final String PCI_ROOT = "pciroot"; public static final String CURRENT_PASSWORD = "currentpassword"; public static final String SHOULD_UPDATE_PASSWORD = "update_passwd_on_host"; public static final String PASSWORD_ENABLED = "passwordenabled"; public static final String SSHKEY_ENABLED = "sshkeyenabled"; public static final String PATH = "path"; + public static final String PATH_READY = "pathready"; public static final String PAYLOAD = "payload"; + public static final String PAYLOAD_URL = "payloadurl"; + public static final String PEERS = "peers"; + public static final String PEER_ID = "peerid"; + public static final String PEER_NAME = "peername"; + public static final String PEER_MSID = "peermsid"; + public static final String PEER_RUNID = "peerrunid"; + public static final String PEER_SERVICE_IP = "peerserviceip"; + public static final String PEER_SERVICE_PORT = "peerserviceport"; + public static final String PEER_STATE = "peerstate"; public static final String POD_ID = "podid"; public static final String POD_NAME = "podname"; public static final String POD_IDS = "podids"; @@ -361,6 +482,8 @@ public class ApiConstants { public static final String POST_URL = "postURL"; public static final String POWER_STATE = "powerstate"; public static final String PRECEDENCE = "precedence"; + public static final String PREPARE_VM = "preparevm"; + public static final String PRESERVE_IP = "preserveip"; public static final String PRIVATE_INTERFACE = "privateinterface"; public static final String PRIVATE_IP = "privateip"; public static final String PRIVATE_PORT = "privateport"; @@ -376,10 +499,17 @@ public class ApiConstants { public static final String PUBLIC_START_PORT = "publicport"; public static final String PUBLIC_END_PORT = "publicendport"; public static final String PUBLIC_ZONE = "publiczone"; + public static final String PURGE_RESOURCES = "purgeresources"; + public static final String RAM = "RAM"; + public static final String REBALANCE = "rebalance"; public static final String RECEIVED_BYTES = "receivedbytes"; public static final String RECONNECT = "reconnect"; public static final String RECOVER = "recover"; + public static final String REPAIR = "repair"; + public static final String REPETITION_ALLOWED = "repetitionallowed"; public static final String REQUIRES_HVM = "requireshvm"; + public static final String RESOURCES = "resources"; + public static final String RESOURCE_COUNT = "resourcecount"; public static final String RESOURCE_NAME = "resourcename"; public static final String RESOURCE_TYPE = "resourcetype"; public static final String RESOURCE_TYPE_NAME = "resourcetypename"; @@ -390,13 +520,13 @@ public class ApiConstants { public static final String REGISTERED = "registered"; public static final String QUALIFIERS = "qualifiers"; public static final String QUERY_FILTER = "queryfilter"; + public static final String QUIESCE_VM = "quiescevm"; public static final String SCHEDULE = "schedule"; + public static final String SCHEDULE_ID = "scheduleid"; public static final String SCOPE = "scope"; - public static final String SECRET_KEY = "usersecretkey"; - public static final String SECONDARY_IP = "secondaryip"; - public static final String SINCE = "since"; - public static final String KEY = "key"; + public static final String USER_SECRET_KEY = "usersecretkey"; public static final String SEARCH_BASE = "searchbase"; + public static final String SECONDARY_IP = "secondaryip"; public static final String SECURITY_GROUP_IDS = "securitygroupids"; public static final String SECURITY_GROUP_NAMES = "securitygroupnames"; public static final String SECURITY_GROUP_NAME = "securitygroupname"; @@ -406,22 +536,32 @@ public class ApiConstants { public static final String SERIAL = "serial"; public static final String SERVICE_IP = "serviceip"; public static final String SERVICE_OFFERING_ID = "serviceofferingid"; + public static final String SERVICE_OFFERING_NAME = "serviceofferingname"; public static final String SESSIONKEY = "sessionkey"; public static final String SHOW_CAPACITIES = "showcapacities"; public static final String SHOW_REMOVED = "showremoved"; 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"; + public static final String SITE_NAME = "sitename"; public static final String SIZE = "size"; + public static final String SIZEGB = "sizegb"; public static final String SNAPSHOT = "snapshot"; public static final String SNAPSHOT_ID = "snapshotid"; public static final String SNAPSHOT_POLICY_ID = "snapshotpolicyid"; public static final String SNAPSHOT_TYPE = "snapshottype"; public static final String SNAPSHOT_QUIESCEVM = "quiescevm"; - public static final String SUPPORTS_STORAGE_SNAPSHOT = "supportsstoragesnapshot"; + + public static final String USE_STORAGE_REPLICATION = "usestoragereplication"; + + public static final String SOURCE_CIDR_LIST = "sourcecidrlist"; public static final String SOURCE_ZONE_ID = "sourcezoneid"; + public static final String SSL_VERIFICATION = "sslverification"; + public static final String START_ASN = "startasn"; public static final String START_DATE = "startdate"; public static final String START_ID = "startid"; public static final String START_IP = "startip"; @@ -434,28 +574,44 @@ public class ApiConstants { public static final String STORAGE_POLICY = "storagepolicy"; public static final String STORAGE_MOTION_ENABLED = "storagemotionenabled"; public static final String STORAGE_CAPABILITIES = "storagecapabilities"; + public static final String STORAGE_CUSTOM_STATS = "storagecustomstats"; public static final String SUBNET = "subnet"; public static final String OWNER = "owner"; public static final String SWAP_OWNER = "swapowner"; public static final String SYSTEM_VM_TYPE = "systemvmtype"; public static final String TAGS = "tags"; public static final String STORAGE_TAGS = "storagetags"; + public static final String STORAGE_ACCESS_GROUPS = "storageaccessgroups"; + public static final String STORAGE_ACCESS_GROUP = "storageaccessgroup"; + public static final String CLUSTER_STORAGE_ACCESS_GROUPS = "clusterstorageaccessgroups"; + public static final String POD_STORAGE_ACCESS_GROUPS = "podstorageaccessgroups"; + public static final String ZONE_STORAGE_ACCESS_GROUPS = "zonestorageaccessgroups"; + public static final String SUCCESS = "success"; + public static final String SUCCESS_MESSAGE = "successmessage"; + public static final String SUITABLE_FOR_VM = "suitableforvirtualmachine"; + public static final String SUPPORTS_STORAGE_SNAPSHOT = "supportsstoragesnapshot"; public static final String TARGET_IQN = "targetiqn"; + 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"; public static final String TEMPLATE_NAME = "templatename"; public static final String TEMPLATE_TYPE = "templatetype"; + public static final String TEMPLATE_FORMAT = "templateformat"; public static final String TIMEOUT = "timeout"; public static final String TIMEZONE = "timezone"; public static final String TIMEZONEOFFSET = "timezoneoffset"; + public static final String TENANT_NAME = "tenantname"; + public static final String TOTAL = "total"; public static final String TOTAL_SUBNETS = "totalsubnets"; public static final String TYPE = "type"; public static final String TRUST_STORE = "truststore"; public static final String TRUST_STORE_PASSWORD = "truststorepass"; public static final String URL = "url"; public static final String USAGE_INTERFACE = "usageinterface"; + public static final String USED = "used"; public static final String USED_SUBNETS = "usedsubnets"; + public static final String USED_IOPS = "usediops"; public static final String USER_DATA = "userdata"; public static final String USER_DATA_NAME = "userdataname"; @@ -472,14 +628,23 @@ public class ApiConstants { public static final String USER_CONFIGURABLE = "userconfigurable"; public static final String USER_SECURITY_GROUP_LIST = "usersecuritygrouplist"; public static final String USE_VIRTUAL_NETWORK = "usevirtualnetwork"; + public static final String USE_VIRTUAL_ROUTER_IP_RESOLVER = "userouteripresolver"; public static final String UPDATE_IN_SEQUENCE = "updateinsequence"; + public static final String VALIDATION_FORMAT = "validationformat"; public static final String VALUE = "value"; + public static final String VALUE_OPTIONS = "valueoptions"; + public static final String VENDOR_ID = "vendorid"; + public static final String VENDOR_NAME = "vendorname"; + public static final String VGPU_PROFILE_ID = "vgpuprofileid"; + public static final String VGPU_PROFILE_NAME = "vgpuprofilename"; + public static final String VIRTUAL_MACHINE = "virtualmachine"; public static final String VIRTUAL_MACHINE_ID = "virtualmachineid"; public static final String VIRTUAL_MACHINE_IDS = "virtualmachineids"; public static final String VIRTUAL_MACHINE_NAME = "virtualmachinename"; public static final String VIRTUAL_MACHINE_ID_IP = "vmidipmap"; public static final String VIRTUAL_MACHINE_COUNT = "virtualmachinecount"; public static final String VIRTUAL_MACHINE_TYPE = "virtualmachinetype"; + 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_TYPE = "usagetype"; @@ -487,21 +652,33 @@ public class ApiConstants { public static final String VLAN = "vlan"; public static final String VLAN_RANGE = "vlanrange"; + public static final String WORKER_SERVICE_OFFERING_ID = "workerofferingid"; + public static final String WORKER_SERVICE_OFFERING_NAME = "workerofferingname"; + public static final String CONTROL_SERVICE_OFFERING_ID = "controlofferingid"; + public static final String CONTROL_SERVICE_OFFERING_NAME = "controlofferingname"; + public static final String ETCD_SERVICE_OFFERING_ID = "etcdofferingid"; + public static final String ETCD_SERVICE_OFFERING_NAME = "etcdofferingname"; public static final String REMOVE_VLAN = "removevlan"; public static final String VLAN_ID = "vlanid"; public static final String ISOLATED_PVLAN = "isolatedpvlan"; public static final String ISOLATED_PVLAN_TYPE = "isolatedpvlantype"; public static final String ISOLATION_URI = "isolationuri"; + public static final String IS_ALLOCATED = "isallocated"; public static final String IS_DEDICATED = "isdedicated"; public static final String TAKEN = "taken"; public static final String VM_AVAILABLE = "vmavailable"; + public static final String VM_DETAILS = "vmdetails"; public static final String VM_LIMIT = "vmlimit"; public static final String VM_TOTAL = "vmtotal"; + public static final String VM_SETTINGS = "vmsettings"; public static final String VM_TYPE = "vmtype"; public static final String VNET = "vnet"; public static final String IS_VOLATILE = "isvolatile"; public static final String VOLUME_ID = "volumeid"; public static final String VOLUMES = "volumes"; + public static final String VOLUME_CHECK_RESULT = "volumecheckresult"; + public static final String VOLUME_REPAIR_RESULT = "volumerepairresult"; + public static final String ZONE = "zone"; public static final String ZONE_ID = "zoneid"; public static final String ZONE_NAME = "zonename"; @@ -517,6 +694,7 @@ public class ApiConstants { public static final String NETWORK_ID = "networkid"; public static final String NETWORK_FILTER = "networkfilter"; public static final String NIC_ID = "nicid"; + public static final String SPECIFY_AS_NUMBER = "specifyasnumber"; public static final String SPECIFY_VLAN = "specifyvlan"; public static final String IS_DEFAULT = "isdefault"; public static final String IS_SYSTEM = "issystem"; @@ -547,6 +725,8 @@ public class ApiConstants { public static final String ALLOCATION_STATE = "allocationstate"; public static final String MANAGED_STATE = "managedstate"; public static final String MANAGEMENT_SERVER_ID = "managementserverid"; + public static final String MANAGEMENT_SERVER_NAME = "managementservername"; + public static final String STORAGE = "storage"; public static final String STORAGE_ID = "storageid"; public static final String PING_STORAGE_SERVER_IP = "pingstorageserverip"; public static final String PING_DIR = "pingdir"; @@ -558,13 +738,14 @@ public class ApiConstants { public static final String NETWORK_DEVICE_PARAMETER_LIST = "networkdeviceparameterlist"; public static final String ZONE_TOKEN = "zonetoken"; public static final String DHCP_PROVIDER = "dhcpprovider"; - public static final String RESULT = "success"; + public static final String RESULT = "result"; public static final String RESUME = "resume"; public static final String LUN_ID = "lunId"; public static final String IQN = "iqn"; public static final String AGGREGATE_NAME = "aggregatename"; public static final String POOL_NAME = "poolname"; public static final String VOLUME_NAME = "volumename"; + public static final String VOLUME_STATE = "volumestate"; public static final String SNAPSHOT_POLICY = "snapshotpolicy"; public static final String SNAPSHOT_RESERVATION = "snapshotreservation"; public static final String IP_NETWORK_LIST = "iptonetworklist"; @@ -587,6 +768,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"; @@ -655,6 +837,8 @@ public class ApiConstants { public static final String ASSOCIATED_NETWORK = "associatednetwork"; public static final String ASSOCIATED_NETWORK_ID = "associatednetworkid"; public static final String ASSOCIATED_NETWORK_NAME = "associatednetworkname"; + public static final String ASSOCIATED_VPC_ID = "associatedvpcid"; + public static final String ASSOCIATED_VPC_NAME = "associatedvpcname"; public static final String SOURCE_NAT_SUPPORTED = "sourcenatsupported"; public static final String RESOURCE_STATE = "resourcestate"; public static final String PROJECT_INVITE_REQUIRED = "projectinviterequired"; @@ -667,8 +851,12 @@ public class ApiConstants { public static final String TRAFFIC_TYPE_IMPLEMENTOR = "traffictypeimplementor"; public static final String KEYWORD = "keyword"; public static final String LIST_ALL = "listall"; + public static final String LIST_ONLY_REMOVED = "listonlyremoved"; public static final String LIST_SYSTEM_VMS = "listsystemvms"; + public static final String LIST_VM_DETAILS = "listvmdetails"; public static final String IP_RANGES = "ipranges"; + public static final String IPV4_ROUTING = "ip4routing"; + public static final String IPV4_ROUTES = "ip4routes"; public static final String IPV6_ROUTING = "ip6routing"; public static final String IPV6_ROUTES = "ip6routes"; public static final String SPECIFY_IP_RANGES = "specifyipranges"; @@ -696,6 +884,12 @@ public class ApiConstants { public static final String VSWITCH_TYPE_PUBLIC_TRAFFIC = "publicvswitchtype"; public static final String VSWITCH_NAME_GUEST_TRAFFIC = "guestvswitchname"; public static final String VSWITCH_NAME_PUBLIC_TRAFFIC = "publicvswitchname"; + + // NSX + public static final String EDGE_CLUSTER = "edgecluster"; + public static final String TIER0_GATEWAY = "tier0gateway"; + + public static final String TRANSPORT_ZONE = "transportzone"; // Tungsten-Fabric public static final String TUNGSTEN_VIRTUAL_ROUTER_UUID = "tungstenvirtualrouteruuid"; public static final String TUNGSTEN_PROVIDER_HOSTNAME = "tungstenproviderhostname"; @@ -719,6 +913,7 @@ public class ApiConstants { public static final String POLICY_UUID = "policyuuid"; public static final String RULE_UUID = "ruleuuid"; public static final String DIRECTION = "direction"; + public static final String TAGGED_RESOURCES = "taggedresources"; public static final String TAG_UUID = "taguuid"; public static final String TAG_TYPE = "tagtype"; public static final String TAG_VALUE = "tagvalue"; @@ -794,6 +989,8 @@ public class ApiConstants { public static final String NETWORK = "network"; public static final String VPC_ID = "vpcid"; public static final String VPC_NAME = "vpcname"; + public static final String VPC_GATEWAY_ID = "vpcgatewayid"; + public static final String VPC_GATEWAY_IP = "vpcgatewayip"; public static final String GATEWAY_ID = "gatewayid"; public static final String CAN_USE_FOR_DEPLOY = "canusefordeploy"; public static final String RESOURCE_IDS = "resourceids"; @@ -816,6 +1013,10 @@ public class ApiConstants { public static final String FORCE_ENCAP = "forceencap"; public static final String SPLIT_CONNECTIONS = "splitconnections"; public static final String FOR_VPC = "forvpc"; + public static final String FOR_NSX = "fornsx"; + public static final String FOR_CKS = "forcks"; + public static final String NSX_SUPPORT_LB = "nsxsupportlb"; + public static final String NSX_SUPPORTS_INTERNAL_LB = "nsxsupportsinternallb"; public static final String FOR_TUNGSTEN = "fortungsten"; public static final String SHRINK_OK = "shrinkok"; public static final String NICIRA_NVP_DEVICE_ID = "nvpdeviceid"; @@ -825,8 +1026,13 @@ public class ApiConstants { public static final String NICIRA_NVP_L2_GATEWAYSERVICE_UUID = "l2gatewayserviceuuid"; public static final String NSX_LOGICAL_SWITCH = "nsxlogicalswitch"; public static final String NSX_LOGICAL_SWITCH_PORT = "nsxlogicalswitchport"; + public static final String NSX_PROVIDER_UUID = "nsxprovideruuid"; + public static final String NSX_PROVIDER_HOSTNAME = "nsxproviderhostname"; + + 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"; @@ -913,7 +1119,9 @@ public class ApiConstants { public static final String ACL_NAME = "aclname"; public static final String NUMBER = "number"; public static final String IS_DYNAMICALLY_SCALABLE = "isdynamicallyscalable"; + public static final String ROUTED_MODE_ENABLED = "routedmodeenabled"; public static final String ROUTING = "isrouting"; + public static final String ROUTING_MODE = "routingmode"; public static final String MAX_CONNECTIONS = "maxconnections"; public static final String SERVICE_STATE = "servicestate"; @@ -926,6 +1134,7 @@ public class ApiConstants { public static final String RESOURCE_DETAILS = "resourcedetails"; public static final String RESOURCE_ICON = "icon"; + public static final String RESOURCE_MAP = "resourcemap"; public static final String EXPUNGE = "expunge"; public static final String FOR_DISPLAY = "fordisplay"; public static final String PASSIVE = "passive"; @@ -949,6 +1158,7 @@ public class ApiConstants { public static final String SUPPORTS_REGION_LEVEL_VPC = "supportsregionLevelvpc"; public static final String SUPPORTS_STRECHED_L2_SUBNET = "supportsstrechedl2subnet"; public static final String SUPPORTS_PUBLIC_ACCESS = "supportspublicaccess"; + public static final String SUPPORTS_INTERNAL_LB = "supportsinternallb"; public static final String SUPPORTS_VM_AUTOSCALING = "supportsvmautoscaling"; public static final String REGION_LEVEL_VPC = "regionlevelvpc"; public static final String STRECHED_L2_SUBNET = "strechedl2subnet"; @@ -956,10 +1166,14 @@ public class ApiConstants { public static final String NETWORK_SPANNED_ZONES = "zonesnetworkspans"; public static final String METADATA = "metadata"; public static final String PHYSICAL_SIZE = "physicalsize"; + public static final String CHAIN_SIZE = "chainsize"; public static final String OVM3_POOL = "ovm3pool"; public static final String OVM3_CLUSTER = "ovm3cluster"; public static final String OVM3_VIP = "ovm3vip"; public static final String CLEAN_UP_DETAILS = "cleanupdetails"; + public static final String CLEAN_UP_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 NETSCALER_CONTROLCENTER_ID = "netscalercontrolcenterid"; public static final String NETSCALER_SERVICEPACKAGE_ID = "netscalerservicepackageid"; @@ -967,6 +1181,7 @@ public class ApiConstants { public static final String ZONE_ID_LIST = "zoneids"; public static final String DESTINATION_ZONE_ID_LIST = "destzoneids"; + public static final String STORAGE_ID_LIST = "storageids"; public static final String ADMIN = "admin"; public static final String CHECKSUM_PARAMETER_PREFIX_DESCRIPTION = "The parameter containing the checksum will be considered a MD5sum if it is not prefixed\n" + " and just a plain ascii/utf8 representation of a hexadecimal string. If it is required to\n" @@ -1008,12 +1223,17 @@ 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"; public static final String MASTER_NODES = "masternodes"; public static final String NODE_IDS = "nodeids"; public static final String CONTROL_NODES = "controlnodes"; + public static final String ETCD_NODES = "etcdnodes"; + public static final String EXTERNAL_NODES = "externalnodes"; + public static final String IS_EXTERNAL_NODE = "isexternalnode"; + public static final String IS_ETCD_NODE = "isetcdnode"; public static final String MIN_SEMANTIC_VERSION = "minimumsemanticversion"; public static final String MIN_KUBERNETES_VERSION_ID = "minimumkubernetesversionid"; public static final String NODE_ROOT_DISK_SIZE = "noderootdisksize"; @@ -1022,6 +1242,15 @@ public class ApiConstants { public static final String AUTOSCALING_ENABLED = "autoscalingenabled"; public static final String MIN_SIZE = "minsize"; public static final String MAX_SIZE = "maxsize"; + public static final String NODE_TYPE_OFFERING_MAP = "nodeofferings"; + public static final String NODE_TYPE_TEMPLATE_MAP = "nodetemplates"; + public static final String 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"; @@ -1043,21 +1272,25 @@ 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"; public static final String LIST_IDPS = "listIdps"; - public static final String READY_FOR_SHUTDOWN = "readyforshutdown"; + public static final String MAINTENANCE_INITIATED = "maintenanceinitiated"; public static final String SHUTDOWN_TRIGGERED = "shutdowntriggered"; + public static final String READY_FOR_SHUTDOWN = "readyforshutdown"; public static final String PENDING_JOBS_COUNT = "pendingjobscount"; + public static final String AGENTS_COUNT = "agentscount"; + public static final String AGENTS = "agents"; + public static final String LAST_AGENTS = "lastagents"; public static final String PUBLIC_MTU = "publicmtu"; public static final String PRIVATE_MTU = "privatemtu"; public static final String MTU = "mtu"; public static final String AUTO_ENABLE_KVM_HOST = "autoenablekvmhost"; public static final String LIST_APIS = "listApis"; - public static final String OBJECT_STORAGE_ID = "objectstorageid"; public static final String VERSIONING = "versioning"; public static final String OBJECT_LOCKING = "objectlocking"; public static final String ENCRYPTION = "encryption"; @@ -1067,14 +1300,16 @@ public class ApiConstants { public static final String SOURCE_NAT_IP = "sourcenatipaddress"; public static final String SOURCE_NAT_IP_ID = "sourcenatipaddressid"; public static final String HAS_RULES = "hasrules"; + public static final String NSX_DETAIL_KEY = "forNsx"; + public static final String NETRIS_DETAIL_KEY = "forNetris"; + public static final String NETRIS_TAG = "netristag"; + public static final String NETRIS_VXLAN_ID = "netrisvxlanid"; + public static final String NETRIS_URL = "netrisurl"; public static final String DISK_PATH = "diskpath"; public static final String IMPORT_SOURCE = "importsource"; public static final String TEMP_PATH = "temppath"; - public static final String OBJECT_STORAGE = "objectstore"; - 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 MANAGEMENT = "management"; public static final String IS_VNF = "isvnf"; public static final String VNF_NICS = "vnfnics"; @@ -1091,6 +1326,64 @@ public class ApiConstants { public static final String PARAMETER_DESCRIPTION_IS_TAG_A_RULE = "Whether the informed tag is a JS interpretable rule or not."; + public static final String WEBHOOK_ID = "webhookid"; + public static final String WEBHOOK_NAME = "webhookname"; + + public static final String NFS_MOUNT_OPTIONS = "nfsmountopts"; + public static final String MOUNT_OPTIONS = "mountopts"; + + public static final String SHAREDFSVM_MIN_CPU_COUNT = "sharedfsvmmincpucount"; + public static final String SHAREDFSVM_MIN_RAM_SIZE = "sharedfsvmminramsize"; + + // Object Storage related + public static final String BUCKET_AVAILABLE = "bucketavailable"; + public static final String BUCKET_LIMIT = "bucketlimit"; + public static final String BUCKET_TOTAL = "buckettotal"; + public static final String OBJECT_STORAGE_ID = "objectstorageid"; + public static final String OBJECT_STORAGE = "objectstore"; + public static final String OBJECT_STORAGE_AVAILABLE = "objectstorageavailable"; + public static final String OBJECT_STORAGE_LIMIT = "objectstoragelimit"; + public static final String OBJECT_STORAGE_TOTAL = "objectstoragetotal"; + + 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 " + + "value will be applied."; + + public static final String PARAMETER_DESCRIPTION_START_DATE_POSSIBLE_FORMATS = "The recommended format is \"yyyy-MM-dd'T'HH:mm:ssZ\" (e.g.: \"2023-01-01T12:00:00+0100\"); " + + "however, the following formats are also accepted: \"yyyy-MM-dd HH:mm:ss\" (e.g.: \"2023-01-01 12:00:00\") and \"yyyy-MM-dd\" (e.g.: \"2023-01-01\" - if the time is not " + + "added, it will be interpreted as \"00:00:00\"). If the recommended format is not used, the date will be considered in the server timezone."; + + public static final String PARAMETER_DESCRIPTION_END_DATE_POSSIBLE_FORMATS = "The recommended format is \"yyyy-MM-dd'T'HH:mm:ssZ\" (e.g.: \"2023-01-01T12:00:00+0100\"); " + + "however, the following formats are also accepted: \"yyyy-MM-dd HH:mm:ss\" (e.g.: \"2023-01-01 12:00:00\") and \"yyyy-MM-dd\" (e.g.: \"2023-01-01\" - if the time is not " + + "added, it will be interpreted as \"23:59:59\"). If the recommended format is not used, the date will be considered in the server timezone."; + + public static final String PARAMETER_DESCRIPTION_MAX_BACKUPS = "The maximum number of backups to keep for a VM. " + + "If \"0\", no retention policy will be applied and, thus, no backups from the schedule will be automatically deleted. " + + "This parameter is only supported for the Dummy, NAS and EMC Networker backup provider."; + + public static final String VMWARE_DC = "vmwaredc"; + + public static final String CSS = "css"; + + public static final String JSON_CONFIGURATION = "jsonconfiguration"; + + public static final String COMMON_NAMES = "commonnames"; + + public static final String COMMON_NAME = "commonname"; + + public static final String DOMAIN_IDS = "domainids"; + + public static final String SHOW_PUBLIC = "showpublic"; + + public static final String LIST_ONLY_DEFAULT_THEME = "listonlydefaulttheme"; + + public static final String RECURSIVE_DOMAINS = "recursivedomains"; + + public static final String VPN_CUSTOMER_GATEWAY_PARAMETERS = "vpncustomergatewayparameters"; + public static final String OBSOLETE_PARAMETERS = "obsoleteparameters"; + public static final String EXCLUDED_PARAMETERS = "excludedparameters"; + /** * 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). @@ -1142,4 +1435,34 @@ public enum VMDetails { public enum DomainDetails { all, resource, min; } + + public enum ExtensionDetails { + all, resource, external, min; + } + + public enum ApiKeyAccess { + DISABLED(false), + ENABLED(true), + INHERIT(null); + + Boolean apiKeyAccess; + + ApiKeyAccess(Boolean keyAccess) { + apiKeyAccess = keyAccess; + } + + public Boolean toBoolean() { + return apiKeyAccess; + } + + public static ApiKeyAccess fromBoolean(Boolean value) { + if (value == null) { + return INHERIT; + } else if (value) { + return ENABLED; + } else { + return DISABLED; + } + } + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiErrorCode.java b/api/src/main/java/org/apache/cloudstack/api/ApiErrorCode.java index d4fdeddc9a95..616c37484d87 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiErrorCode.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiErrorCode.java @@ -22,6 +22,7 @@ */ public enum ApiErrorCode { + BAD_REQUEST(400), UNAUTHORIZED(401), UNAUTHORIZED2FA(511), METHOD_NOT_ALLOWED(405), @@ -30,6 +31,7 @@ public enum ApiErrorCode { UNSUPPORTED_ACTION_ERROR(432), API_LIMIT_EXCEED(429), + SERVICE_UNAVAILABLE(503), INTERNAL_ERROR(530), ACCOUNT_ERROR(531), ACCOUNT_RESOURCE_LIMIT_ERROR(532), diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiServerService.java b/api/src/main/java/org/apache/cloudstack/api/ApiServerService.java index 54fda7e36b82..18c96c371591 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiServerService.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiServerService.java @@ -21,7 +21,9 @@ import javax.servlet.http.HttpSession; +import com.cloud.domain.Domain; import com.cloud.exception.CloudAuthenticationException; +import com.cloud.user.UserAccount; public interface ApiServerService { public boolean verifyRequest(Map requestParameters, Long userId, InetAddress remoteAddress) throws ServerApiException; @@ -42,4 +44,12 @@ public ResponseObject loginUser(HttpSession session, String username, String pas public String handleRequest(Map params, String responseType, StringBuilder auditTrailSb) throws ServerApiException; public Class getCmdClass(String cmdName); + + boolean forgotPassword(UserAccount userAccount, Domain domain); + + boolean resetPassword(UserAccount userAccount, String token, String password); + + String getDomainId(Map params); + + boolean isPostRequestsAndTimestampsEnforced(); } 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..30baa71d6d85 100644 --- a/api/src/main/java/org/apache/cloudstack/api/BaseAsyncCreateCustomIdCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseAsyncCreateCustomIdCmd.java @@ -19,7 +19,7 @@ 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") + description = "An optional field, in case you want to set a custom id to the resource. Allowed to Root Admins only") private String customId; public String getCustomId() { 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 323fd4e6f64d..e495cf284130 100644 --- a/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java @@ -27,18 +27,22 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.UUID; import java.util.regex.Pattern; import javax.inject.Inject; +import com.cloud.bgp.BGPService; 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.network.element.InternalLoadBalancerElementService; +import org.apache.cloudstack.gpu.GpuService; +import org.apache.cloudstack.network.RoutedIpv4Manager; import org.apache.cloudstack.network.lb.ApplicationLoadBalancerService; import org.apache.cloudstack.network.lb.InternalLoadBalancerVMService; import org.apache.cloudstack.query.QueryService; @@ -93,6 +97,7 @@ import com.cloud.utils.db.EntityManager; import com.cloud.utils.db.UUIDManager; import com.cloud.vm.UserVmService; +import com.cloud.vm.VmDetailConstants; import com.cloud.vm.snapshot.VMSnapshotService; public abstract class BaseCmd { @@ -129,6 +134,8 @@ public static enum CommandType { @Inject public UserVmService _userVmService; @Inject + public GpuService gpuService; + @Inject public ManagementService _mgr; @Inject public StorageService _storageService; @@ -201,8 +208,6 @@ public static enum CommandType { @Inject public AffinityGroupService _affinityGroupService; @Inject - public InternalLoadBalancerElementService _internalLbElementSvc; - @Inject public InternalLoadBalancerVMService _internalLbSvc; @Inject public NetworkModel _ntwkModel; @@ -217,10 +222,16 @@ public static enum CommandType { @Inject public Ipv6Service ipv6Service; @Inject + public ApiKeyPairService apiKeyPairService; + @Inject public VnfTemplateManager vnfTemplateManager; @Inject public BucketApiService _bucketService; + @Inject + public BGPService bgpService; + @Inject + public RoutedIpv4Manager routedIpv4Manager; public abstract void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException; @@ -375,7 +386,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()); } } @@ -481,4 +492,24 @@ public Map convertDetailsToMap(Map details) { } return detailsMap; } + + public Map convertExternalDetailsToMap(Map externalDetails) { + Map customparameterMap = convertDetailsToMap(externalDetails); + Map details = new HashMap<>(); + for (String key : customparameterMap.keySet()) { + String value = customparameterMap.get(key); + details.put(VmDetailConstants.EXTERNAL_DETAIL_PREFIX + key, value); + } + return details; + } + + public String getResourceUuid(String parameterName) { + UUID resourceUuid = CallContext.current().getApiResourceUuid(parameterName); + + if (resourceUuid != null) { + return resourceUuid.toString(); + } + + return null; + } } 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 e3aead6881ba..94c5d8ff39fc 100644 --- a/api/src/main/java/org/apache/cloudstack/api/BaseUpdateTemplateOrIsoCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseUpdateTemplateOrIsoCmd.java @@ -16,8 +16,10 @@ // under the License. package org.apache.cloudstack.api; +import com.cloud.cpu.CPU; import org.apache.cloudstack.api.response.GuestOSResponse; import org.apache.cloudstack.api.response.TemplateResponse; +import org.apache.commons.lang3.StringUtils; import java.util.Collection; import java.util.Map; @@ -28,45 +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.FORMAT, type = CommandType.STRING, description = "the format for the image") + @Parameter(name = ApiConstants.FORCE_UPDATE_OS_TYPE, type = CommandType.BOOLEAN, since = "4.21", description = "Force OS type update. Warning: Updating OS type will " + + "update the guest OS configuration for all the existing Instances deployed with this template/iso, which may affect their behavior.") + private Boolean forceUpdateOsType; + + @Parameter(name = ApiConstants.FORMAT, type = CommandType.STRING, description = "The format for the image") private String format; - @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") @@ -74,9 +80,14 @@ 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, s390x", + since = "4.20") + private String arch; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -105,6 +116,10 @@ public Long getOsTypeId() { return osTypeId; } + public Boolean getForceUpdateOsType() { + return forceUpdateOsType; + } + public Boolean getPasswordEnabled() { return passwordEnabled; } @@ -138,7 +153,14 @@ 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() { + if (StringUtils.isBlank(arch)) { + return null; + } + return CPU.CPUArch.fromType(arch); } } 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/InternalIdentity.java b/api/src/main/java/org/apache/cloudstack/api/InternalIdentity.java index 4149dd1c8465..ce214e05b4f1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/InternalIdentity.java +++ b/api/src/main/java/org/apache/cloudstack/api/InternalIdentity.java @@ -25,4 +25,18 @@ public interface InternalIdentity extends Serializable { long getId(); + + /* + Helper method to add conditions in joins where some column name is equal to a string value + */ + default Object setString(String str) { + return null; + } + + /* + Helper method to add conditions in joins where some column name is equal to a long value + */ + default Object setLong(Long l) { + return null; + } } 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 ef759aaf9c3e..338c1e738dfc 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java @@ -22,13 +22,18 @@ import java.util.Map; import java.util.Set; -import org.apache.cloudstack.storage.object.Bucket; +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; import org.apache.cloudstack.api.ApiConstants.VMDetails; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.command.user.job.QueryAsyncJobResultCmd; +import org.apache.cloudstack.api.response.ASNRangeResponse; +import org.apache.cloudstack.api.response.ASNumberResponse; import org.apache.cloudstack.api.response.AccountResponse; import org.apache.cloudstack.api.response.ApplicationLoadBalancerResponse; import org.apache.cloudstack.api.response.AsyncJobResponse; @@ -36,8 +41,9 @@ import org.apache.cloudstack.api.response.AutoScaleVmGroupResponse; import org.apache.cloudstack.api.response.AutoScaleVmProfileResponse; import org.apache.cloudstack.api.response.BackupOfferingResponse; -import org.apache.cloudstack.api.response.BackupResponse; +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; @@ -57,21 +63,24 @@ import org.apache.cloudstack.api.response.FirewallResponse; import org.apache.cloudstack.api.response.FirewallRuleResponse; import org.apache.cloudstack.api.response.GlobalLoadBalancerResponse; +import org.apache.cloudstack.api.response.GuestOSCategoryResponse; import org.apache.cloudstack.api.response.GuestOSResponse; import org.apache.cloudstack.api.response.GuestOsMappingResponse; import org.apache.cloudstack.api.response.GuestVlanRangeResponse; import org.apache.cloudstack.api.response.GuestVlanResponse; +import org.apache.cloudstack.api.response.GuiThemeResponse; import org.apache.cloudstack.api.response.HostForMigrationResponse; import org.apache.cloudstack.api.response.HostResponse; import org.apache.cloudstack.api.response.HypervisorCapabilitiesResponse; import org.apache.cloudstack.api.response.HypervisorGuestOsNamesResponse; import org.apache.cloudstack.api.response.IPAddressResponse; -import org.apache.cloudstack.api.response.IpQuarantineResponse; import org.apache.cloudstack.api.response.ImageStoreResponse; import org.apache.cloudstack.api.response.InstanceGroupResponse; import org.apache.cloudstack.api.response.InternalLoadBalancerElementResponse; import org.apache.cloudstack.api.response.IpForwardingRuleResponse; +import org.apache.cloudstack.api.response.IpQuarantineResponse; import org.apache.cloudstack.api.response.IsolationMethodResponse; +import org.apache.cloudstack.api.response.ApiKeyPairResponse; import org.apache.cloudstack.api.response.LBHealthCheckResponse; import org.apache.cloudstack.api.response.LBStickinessResponse; import org.apache.cloudstack.api.response.ListResponse; @@ -108,6 +117,7 @@ import org.apache.cloudstack.api.response.SecurityGroupResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; import org.apache.cloudstack.api.response.ServiceResponse; +import org.apache.cloudstack.api.response.SharedFSResponse; import org.apache.cloudstack.api.response.Site2SiteCustomerGatewayResponse; import org.apache.cloudstack.api.response.Site2SiteVpnConnectionResponse; import org.apache.cloudstack.api.response.Site2SiteVpnGatewayResponse; @@ -137,23 +147,29 @@ import org.apache.cloudstack.api.response.VpcResponse; import org.apache.cloudstack.api.response.VpnUsersResponse; import org.apache.cloudstack.api.response.ZoneResponse; -import org.apache.cloudstack.backup.Backup; import org.apache.cloudstack.backup.BackupOffering; +import org.apache.cloudstack.backup.BackupRepository; import org.apache.cloudstack.backup.BackupSchedule; import org.apache.cloudstack.config.Configuration; import org.apache.cloudstack.config.ConfigurationGroup; import org.apache.cloudstack.direct.download.DirectDownloadCertificate; import org.apache.cloudstack.direct.download.DirectDownloadCertificateHostMap; import org.apache.cloudstack.direct.download.DirectDownloadManager; +import org.apache.cloudstack.gui.theme.GuiThemeJoin; import org.apache.cloudstack.management.ManagementServerHost; import org.apache.cloudstack.network.lb.ApplicationLoadBalancerRule; import org.apache.cloudstack.region.PortableIp; import org.apache.cloudstack.region.PortableIpRange; import org.apache.cloudstack.region.Region; import org.apache.cloudstack.secstorage.heuristics.Heuristic; +import org.apache.cloudstack.storage.object.Bucket; import org.apache.cloudstack.storage.object.ObjectStore; +import org.apache.cloudstack.storage.sharedfs.SharedFS; import org.apache.cloudstack.usage.Usage; +import org.apache.cloudstack.vm.UnmanagedInstanceTO; +import com.cloud.bgp.ASNumber; +import com.cloud.bgp.ASNumberRange; import com.cloud.capacity.Capacity; import com.cloud.configuration.ResourceCount; import com.cloud.configuration.ResourceLimit; @@ -214,10 +230,11 @@ import com.cloud.projects.ProjectInvitation; import com.cloud.region.ha.GlobalLoadBalancerRule; import com.cloud.resource.RollingMaintenanceManager; -import com.cloud.server.ResourceTag; import com.cloud.server.ResourceIcon; +import com.cloud.server.ResourceTag; import com.cloud.storage.GuestOS; import com.cloud.storage.GuestOSHypervisor; +import com.cloud.storage.GuestOsCategory; import com.cloud.storage.ImageStore; import com.cloud.storage.Snapshot; import com.cloud.storage.StoragePool; @@ -231,14 +248,13 @@ import com.cloud.user.UserAccount; import com.cloud.user.UserData; import com.cloud.uservm.UserVm; -import com.cloud.utils.net.Ip; import com.cloud.utils.Pair; +import com.cloud.utils.net.Ip; import com.cloud.vm.InstanceGroup; import com.cloud.vm.Nic; import com.cloud.vm.NicSecondaryIp; import com.cloud.vm.VirtualMachine; import com.cloud.vm.snapshot.VMSnapshot; -import org.apache.cloudstack.vm.UnmanagedInstanceTO; public interface ResponseGenerator { UserResponse createUserResponse(UserAccount user); @@ -301,6 +317,8 @@ public interface ResponseGenerator { PodResponse createPodResponse(Pod pod, Boolean showCapacities); + PodResponse createMinimalPodResponse(Pod pod); + ZoneResponse createZoneResponse(ResponseView view, DataCenter dataCenter, Boolean showCapacities, Boolean showResourceIcon); DataCenterGuestIpv6PrefixResponse createDataCenterGuestIpv6PrefixResponse(DataCenterGuestIpv6Prefix prefix); @@ -315,6 +333,8 @@ public interface ResponseGenerator { ClusterResponse createClusterResponse(Cluster cluster, Boolean showCapacities); + ClusterResponse createMinimalClusterResponse(Cluster cluster); + FirewallRuleResponse createPortForwardingRuleResponse(PortForwardingRule fwRule); IpForwardingRuleResponse createIpForwardingRuleResponse(StaticNatRule fwRule); @@ -345,9 +365,11 @@ public interface ResponseGenerator { SecurityGroupResponse createSecurityGroupResponse(SecurityGroup group); - ExtractResponse createExtractResponse(Long uploadId, Long id, Long zoneId, Long accountId, String mode, String url); + ExtractResponse createImageExtractResponse(Long id, Long zoneId, Long accountId, String mode, String url); + + ExtractResponse createVolumeExtractResponse(Long id, Long zoneId, Long accountId, String mode, String url); - ExtractResponse createExtractResponse(Long id, Long zoneId, Long accountId, String mode, String url); + ExtractResponse createSnapshotExtractResponse(Long id, Long zoneId, Long accountId, String url); String toSerializedString(CreateCmdResponse response, String responseType); @@ -470,6 +492,10 @@ List createTemplateResponses(ResponseView view, VirtualMachine AutoScaleVmGroupResponse createAutoScaleVmGroupResponse(AutoScaleVmGroup vmGroup); + GuestOSCategoryResponse createGuestOSCategoryResponse(GuestOsCategory guestOsCategory); + + GuestOSCategoryResponse createGuestOSCategoryResponse(GuestOsCategory guestOsCategory, boolean showIcon); + GuestOSResponse createGuestOSResponse(GuestOS os); GuestOsMappingResponse createGuestOSMappingResponse(GuestOSHypervisor osHypervisor); @@ -512,8 +538,6 @@ List createTemplateResponses(ResponseView view, VirtualMachine UserDataResponse createUserDataResponse(UserData userData); - BackupResponse createBackupResponse(Backup backup); - BackupScheduleResponse createBackupScheduleResponse(BackupSchedule backup); BackupOfferingResponse createBackupOfferingResponse(BackupOffering policy); @@ -549,4 +573,22 @@ List createTemplateResponses(ResponseView view, VirtualMachine ObjectStoreResponse createObjectStoreResponse(ObjectStore os); BucketResponse createBucketResponse(Bucket bucket); + + ASNRangeResponse createASNumberRangeResponse(ASNumberRange asnRange); + + ASNumberResponse createASNumberResponse(ASNumber asn); + + BackupRepositoryResponse createBackupRepositoryResponse(BackupRepository repository); + + SharedFSResponse createSharedFSResponse(ResponseView view, SharedFS sharedFS); + + void updateTemplateIsoResponsesForIcons(List responses, ResourceTag.ResourceObjectType type); + + GuiThemeResponse createGuiThemeResponse(GuiThemeJoin guiThemeJoin); + + ConsoleSessionResponse createConsoleSessionResponse(ConsoleSession consoleSession, ResponseView responseView); + + ApiKeyPairResponse createKeyPairResponse(ApiKeyPair keyPair); + + ListResponse createKeypairPermissionsResponse(List permissions); } diff --git a/api/src/main/java/org/apache/cloudstack/api/auth/APIAuthenticationType.java b/api/src/main/java/org/apache/cloudstack/api/auth/APIAuthenticationType.java index 5ba9d182daa0..1f78708f7e58 100644 --- a/api/src/main/java/org/apache/cloudstack/api/auth/APIAuthenticationType.java +++ b/api/src/main/java/org/apache/cloudstack/api/auth/APIAuthenticationType.java @@ -17,5 +17,5 @@ package org.apache.cloudstack.api.auth; public enum APIAuthenticationType { - LOGIN_API, LOGOUT_API, READONLY_API, LOGIN_2FA_API + LOGIN_API, LOGOUT_API, READONLY_API, LOGIN_2FA_API, PASSWORD_RESET } 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 36e22acff91e..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 @@ -89,12 +89,11 @@ public void execute() { CallContext.current().setEventDetails("Account ID: " + (account != null ? account.getUuid() : getId())); // Account not found is already handled by service boolean result = _regionService.deleteUserAccount(this); - if (result) { - SuccessResponse response = new SuccessResponse(getCommandName()); - setResponseObject(response); - } else { + if (!result) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete user account and all corresponding users"); } + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); } @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 91cbb90e4da4..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 @@ -21,7 +21,9 @@ import javax.inject.Inject; +import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.command.user.UserCmd; import org.apache.cloudstack.api.response.RoleResponse; import org.apache.cloudstack.acl.SecurityChecker.AccessType; @@ -39,9 +41,9 @@ import com.cloud.user.Account; -@APICommand(name = "updateAccount", description = "Updates account information for the authenticated user", responseObject = AccountResponse.class, entityType = {Account.class}, - requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) -public class UpdateAccountCmd extends BaseCmd { +@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 { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// @@ -50,26 +52,29 @@ public class UpdateAccountCmd extends BaseCmd { @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}) + private String apiKeyAccess; + @Inject RegionService _regionService; @@ -109,6 +114,10 @@ public Map getDetails() { return params; } + public String getApiKeyAccess() { + return apiKeyAccess; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -131,11 +140,11 @@ public long getEntityOwnerId() { public void execute() { Account result = _regionService.updateAccount(this); if (result != null){ - AccountResponse response = _responseGenerator.createAccountResponse(ResponseView.Full, result); + AccountResponse response = _responseGenerator.createAccountResponse(getResponseView(), result); 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 new file mode 100644 index 000000000000..2c5659b2bc4b --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/DisableRoleCmd.java @@ -0,0 +1,69 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.acl; + +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.Role; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiArgValidator; +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.RoleResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; + +@APICommand(name = "disableRole", description = "Disables a role", responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.20.0", + authorized = {RoleType.Admin}) +public class DisableRoleCmd extends BaseCmd { + + @Parameter(name = ApiConstants.ID, type = BaseCmd.CommandType.UUID, required = true, entityType = RoleResponse.class, + description = "ID of the role", validations = {ApiArgValidator.PositiveNumber}) + private Long roleId; + + public Long getRoleId() { + return roleId; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + Role role = roleService.findRole(getRoleId()); + if (role == null) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Cannot find the role with provided id"); + } + CallContext.current().setEventDetails("Role ID: " + role.getUuid()); + boolean result = roleService.disableRole(role); + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setSuccess(result); + setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} 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 new file mode 100644 index 000000000000..05dfbe1270fa --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/EnableRoleCmd.java @@ -0,0 +1,69 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.acl; + +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.Role; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiArgValidator; +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.RoleResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; + +@APICommand(name = "enableRole", description = "Enables a role", responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.20.0", + authorized = {RoleType.Admin}) +public class EnableRoleCmd extends BaseCmd { + + @Parameter(name = ApiConstants.ID, type = BaseCmd.CommandType.UUID, required = true, entityType = RoleResponse.class, + description = "ID of the role", validations = {ApiArgValidator.PositiveNumber}) + private Long roleId; + + public Long getRoleId() { + return roleId; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + Role role = roleService.findRole(getRoleId()); + if (role == null) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Cannot find the role with provided id"); + } + CallContext.current().setEventDetails("Role ID: " + role.getUuid()); + boolean result = roleService.enableRole(role); + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setSuccess(result); + setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ListRolesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ListRolesCmd.java index fef2b27eaa5a..d82cc852e4ff 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ListRolesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/ListRolesCmd.java @@ -21,6 +21,7 @@ import java.util.Collections; import java.util.List; +import com.cloud.exception.InvalidParameterValueException; import org.apache.cloudstack.acl.Role; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; @@ -51,6 +52,9 @@ public class ListRolesCmd extends BaseListCmd { @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "List role by role type, valid options are: Admin, ResourceAdmin, DomainAdmin, User.") private String roleType; + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "List role by role type status, valid options are: enabled, disabled") + private String roleState; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -70,6 +74,17 @@ public RoleType getRoleType() { return null; } + public Role.State getRoleState() { + if (roleState == null) { + return null; + } + try { + return Role.State.valueOf(roleState.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new InvalidParameterValueException("Unrecognized role state value"); + } + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -93,6 +108,7 @@ private void setupResponse(final Pair, Integer> roles) { roleResponse.setDescription(role.getDescription()); roleResponse.setIsDefault(role.isDefault()); roleResponse.setPublicRole(role.isPublicRole()); + roleResponse.setState(role.getState().toString()); roleResponse.setObjectName("role"); roleResponses.add(roleResponse); } @@ -104,14 +120,16 @@ private void setupResponse(final Pair, Integer> roles) { @Override public void execute() { Pair, Integer> roles; + Role.State state = getRoleState(); + String roleStateStr = state != null ? state.toString() : null; if (getId() != null && getId() > 0L) { roles = new Pair<>(Collections.singletonList(roleService.findRole(getId(), true)), 1); } else if (StringUtils.isNotBlank(getName()) || StringUtils.isNotBlank(getKeyword())) { - roles = roleService.findRolesByName(getName(), getKeyword(), getStartIndex(), getPageSizeVal()); + roles = roleService.findRolesByName(getName(), getKeyword(), roleStateStr, getStartIndex(), getPageSizeVal()); } else if (getRoleType() != null) { - roles = roleService.findRolesByType(getRoleType(), getStartIndex(), getPageSizeVal()); + roles = roleService.findRolesByType(getRoleType(), roleStateStr, getStartIndex(), getPageSizeVal()); } else { - roles = roleService.listRoles(getStartIndex(), getPageSizeVal()); + roles = roleService.listRoles(roleStateStr, getStartIndex(), getPageSizeVal()); } setupResponse(roles); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/RoleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/RoleCmd.java index 4c317d06b136..b3d816adc3fb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/RoleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/RoleCmd.java @@ -59,6 +59,7 @@ protected void setupResponse(final Role role) { response.setRoleType(role.getRoleType()); response.setDescription(role.getDescription()); response.setPublicRole(role.isPublicRole()); + response.setState(role.getState().toString()); response.setResponseName(getCommandName()); response.setObjectName("role"); setResponseObject(response); 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 c03e6112ea7b..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 @@ -20,6 +20,7 @@ import org.apache.cloudstack.acl.ProjectRole; 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; @@ -40,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; ///////////////////////////////////////////////////// @@ -70,4 +71,13 @@ public long getEntityOwnerId() { return Account.ACCOUNT_ID_SYSTEM; } + @Override + public Long getApiResourceId() { + return getProjectId(); + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.Project; + } } 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 9b6c2e633fc0..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 @@ -22,6 +22,7 @@ import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiArgValidator; +import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.Parameter; @@ -71,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"); @@ -96,4 +97,14 @@ private void setupResponse(final ProjectRolePermission rolePermission, final Pro response.setObjectName("projectrolepermission"); setResponseObject(response); } + + @Override + public Long getApiResourceId() { + return getProjectId(); + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.Project; + } } 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 4bb460c63f71..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 @@ -21,6 +21,7 @@ import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiArgValidator; +import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseCmd; @@ -68,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); @@ -79,4 +80,14 @@ public void execute() { public long getEntityOwnerId() { return CallContext.current().getCallingAccountId(); } + + @Override + public Long getApiResourceId() { + return getProjectId(); + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.Project; + } } 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 8b83253c869b..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 @@ -21,6 +21,7 @@ import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiArgValidator; +import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseCmd; @@ -69,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); @@ -80,4 +81,14 @@ public void execute() { public long getEntityOwnerId() { return CallContext.current().getCallingAccountId(); } + + @Override + public Long getApiResourceId() { + return getProjectId(); + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.Project; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/ListProjectRolesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/ListProjectRolesCmd.java index e876dbc9b58a..dedbb410ea56 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/ListProjectRolesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/ListProjectRolesCmd.java @@ -72,7 +72,7 @@ public String getRoleName() { @Override public void execute() { - List projectRoles; + List projectRoles = new ArrayList<>(); if (getProjectId() != null && getProjectRoleId() != null) { projectRoles = Collections.singletonList(projRoleService.findProjectRole(getProjectRoleId(), getProjectId())); } else if (StringUtils.isNotBlank(getRoleName())) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/UpdateProjectRoleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/UpdateProjectRoleCmd.java index 202daa3d49cc..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 @@ -21,6 +21,7 @@ import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiArgValidator; +import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseCmd; @@ -42,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; ///////////////////////////////////////////////////// @@ -76,4 +77,14 @@ public void execute() { public long getEntityOwnerId() { return 0; } + + @Override + public Long getApiResourceId() { + return getProjectId(); + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.Project; + } } 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 d27235e5aaee..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 @@ -26,6 +26,7 @@ import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiArgValidator; +import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseCmd; @@ -56,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, @@ -114,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 ) { @@ -122,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()); @@ -154,4 +155,14 @@ private boolean updateProjectRolePermissionOrder(ProjectRole projectRole) { public long getEntityOwnerId() { return CallContext.current().getCallingAccountId(); } + + @Override + public Long getApiResourceId() { + return getProjectId(); + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.Project; + } } 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/ImportBackupOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ImportBackupOfferingCmd.java index 7d3902bc4902..f852f7e25776 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", @@ -55,11 +61,11 @@ public class ImportBackupOfferingCmd extends BaseAsyncCmd { //////////////////////////////////////////////////// @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,13 @@ 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") + private List domainIds; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -100,6 +113,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 +156,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/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/bgp/CreateASNRangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/bgp/CreateASNRangeCmd.java new file mode 100644 index 000000000000..beacba850c3f --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/bgp/CreateASNRangeCmd.java @@ -0,0 +1,83 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.bgp; + +import com.cloud.bgp.ASNumberRange; +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.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.ASNRangeResponse; +import org.apache.cloudstack.api.response.ZoneResponse; + +@APICommand(name = "createASNRange", + description = "Creates a range of Autonomous Systems for BGP Dynamic Routing", + responseObject = ASNRangeResponse.class, + entityType = {ASNumberRange.class}, + since = "4.20.0", + authorized = {RoleType.Admin}) +public class CreateASNRangeCmd extends BaseCmd { + + @Parameter(name = ApiConstants.ZONE_ID, type = BaseCmd.CommandType.UUID, entityType = ZoneResponse.class, + description = "the zone ID", required = true) + private Long zoneId; + + @Parameter(name = ApiConstants.START_ASN, type = CommandType.LONG, required=true, description = "the start AS Number") + private Long startASNumber; + + @Parameter(name = ApiConstants.END_ASN, type = CommandType.LONG, required=true, description = "the end AS Number") + private Long endASNumber; + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try { + ASNumberRange asnRange = bgpService.createASNumberRange(zoneId, startASNumber, endASNumber); + ASNRangeResponse response = _responseGenerator.createASNumberRangeResponse(asnRange); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (Exception e) { + String msg = String.format("Cannot create AS Number Range %s-%s for zone %s: %s", startASNumber, endASNumber, zoneId, e.getMessage()); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, msg); + } + } + + public Long getZoneId() { + return zoneId; + } + + public Long getStartASNumber() { + return startASNumber; + } + + public Long getEndASNumber() { + return endASNumber; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/bgp/DeleteASNRangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/bgp/DeleteASNRangeCmd.java new file mode 100644 index 000000000000..33e139315bf6 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/bgp/DeleteASNRangeCmd.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.bgp; + +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.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.ASNRangeResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; + +@APICommand(name = "deleteASNRange", + description = "deletes a range of Autonomous Systems for BGP Dynamic Routing", + responseObject = SuccessResponse.class, + since = "4.20.0", + authorized = {RoleType.Admin}) +public class DeleteASNRangeCmd extends BaseCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + //////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, + type = CommandType.UUID, + entityType = ASNRangeResponse.class, + required = true, + description = "ID of the AS range") + private Long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + if (bgpService.deleteASRange(getId())) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove AS range: " + getId()); + } + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/bgp/ListASNRangesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/bgp/ListASNRangesCmd.java new file mode 100644 index 000000000000..82e545811029 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/bgp/ListASNRangesCmd.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.bgp; + +import com.cloud.bgp.ASNumberRange; +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.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ASNRangeResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ZoneResponse; + +import java.util.ArrayList; +import java.util.List; + +@APICommand(name = "listASNRanges", + description = "List Autonomous Systems Number Ranges", + responseObject = ASNRangeResponse.class, + entityType = {ASNumberRange.class}, + since = "4.20.0", + authorized = {RoleType.Admin}) +public class ListASNRangesCmd extends BaseListCmd { + + @Parameter(name = ApiConstants.ZONE_ID, type = BaseCmd.CommandType.UUID, entityType = ZoneResponse.class, + description = "the zone ID") + private Long zoneId; + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try { + List ranges = bgpService.listASNumberRanges(zoneId); + ListResponse response = new ListResponse<>(); + List responses = new ArrayList<>(); + for (ASNumberRange asnRange : ranges) { + responses.add(_responseGenerator.createASNumberRangeResponse(asnRange)); + } + response.setResponses(responses); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (Exception e) { + String msg = String.format("Error listing AS Number Ranges: %s", e.getMessage()); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, msg); + } + } + + public Long getZoneId() { + return zoneId; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/bgp/ReleaseASNumberCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/bgp/ReleaseASNumberCmd.java new file mode 100644 index 000000000000..687f60dc6da8 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/bgp/ReleaseASNumberCmd.java @@ -0,0 +1,83 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.bgp; + +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.utils.Pair; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.ZoneResponse; + +@APICommand(name = "releaseASNumber", + description = "Releases an AS Number back to the pool", + since = "4.20.0", + authorized = {RoleType.Admin}, + responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false) +public class ReleaseASNumberCmd extends BaseCmd { + + @Parameter(name = ApiConstants.ZONE_ID, type = BaseCmd.CommandType.UUID, entityType = ZoneResponse.class, + description = "the zone ID", required = true) + private Long zoneId; + + @Parameter(name= ApiConstants.AS_NUMBER, type=CommandType.LONG, description="the AS Number to be released", + required = true) + private Long asNumber; + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try { + Pair resultPair = bgpService.releaseASNumber(zoneId, asNumber, false); + Boolean result = resultPair.first(); + if (!result) { + String details = resultPair.second(); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Cannot release AS Number %s: %s", asNumber, details)); + } + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setDisplayText(String.format("AS Number %s is released successfully", asNumber)); + setResponseObject(response); + } catch (Exception e) { + String msg = String.format("Error releasing AS Number %s: %s", asNumber, e.getMessage()); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, msg); + } + } + + public Long getZoneId() { + return zoneId; + } + + public Long getAsNumber() { + return asNumber; + } + + @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..6deaea22ac6c 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, @@ -108,7 +108,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 184a443d9db9..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 @@ -19,20 +19,22 @@ import java.util.ArrayList; import java.util.List; - -import org.apache.cloudstack.api.ApiCommandResourceType; +import java.util.Map; import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.ClusterResponse; +import org.apache.cloudstack.api.response.ExtensionResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.PodResponse; import org.apache.cloudstack.api.response.ZoneResponse; +import com.cloud.cpu.CPU; import com.cloud.exception.DiscoveryException; import com.cloud.exception.ResourceInUseException; import com.cloud.org.Cluster; @@ -42,44 +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") + description = "Hypervisor type of the cluster: XenServer,KVM,VMware,Hyperv,BareMetal,Simulator,Ovm3,External") private String hypervisor; - @Parameter(name = ApiConstants.CLUSTER_TYPE, type = CommandType.STRING, required = true, description = "type of the cluster: CloudManaged, ExternalManaged") + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + 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") 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, @@ -103,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") @@ -112,6 +118,26 @@ public class AddClusterCmd extends BaseCmd { private String ovm3cluster; @Parameter(name = ApiConstants.OVM3_VIP, type = CommandType.STRING, required = false, description = "Ovm3 vip to use for pool (and cluster)") private String ovm3vip; + + @Parameter(name = ApiConstants.STORAGE_ACCESS_GROUPS, + type = CommandType.LIST, collectionType = CommandType.STRING, + description = "comma separated list of storage access groups for the hosts in the cluster", + since = "4.21.0") + private List storageAccessGroups; + + + @Parameter(name = ApiConstants.EXTENSION_ID, + type = CommandType.UUID, entityType = ExtensionResponse.class, + description = "UUID of the extension", + since = "4.21.0") + private Long extensionId; + + @Parameter(name = ApiConstants.EXTERNAL_DETAILS, + type = CommandType.MAP, + description = "Details in key/value pairs to be added to the extension-resource mapping. Use the format externaldetails[i].=. Example: externaldetails[0].endpoint.url=https://example.com", + since = "4.21.0") + protected Map externalDetails; + public String getOvm3Pool() { return ovm3pool; } @@ -178,6 +204,10 @@ public String getHypervisor() { return hypervisor; } + public Long getExtensionId() { + return extensionId; + } + public String getClusterType() { return clusterType; } @@ -186,6 +216,10 @@ public void setClusterType(String type) { this.clusterType = type; } + public List getStorageAccessGroups() { + return storageAccessGroups; + } + @Override public long getEntityOwnerId() { return Account.ACCOUNT_ID_SYSTEM; @@ -204,6 +238,14 @@ public ApiCommandResourceType getApiResourceType() { return ApiCommandResourceType.Cluster; } + public CPU.CPUArch getArch() { + return CPU.CPUArch.fromType(arch); + } + + public Map getExternalDetails() { + return convertDetailsToMap(externalDetails); + } + @Override public void execute() { try { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/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 67d0678410cf..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 @@ -17,8 +17,11 @@ package org.apache.cloudstack.api.command.admin.cluster; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import javax.inject.Inject; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -28,7 +31,13 @@ import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.PodResponse; import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.extension.Extension; +import org.apache.cloudstack.extension.ExtensionHelper; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import com.cloud.cpu.CPU; +import com.cloud.hypervisor.Hypervisor; import com.cloud.org.Cluster; import com.cloud.utils.Pair; @@ -36,38 +45,50 @@ requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListClustersCmd extends BaseListCmd { + @Inject + ExtensionHelper extensionHelper; ///////////////////////////////////////////////////// //////////////// 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, + description = "CPU arch of the clusters", + since = "4.20.1") + private String arch; + + @Parameter(name = ApiConstants.STORAGE_ACCESS_GROUP, type = CommandType.STRING, + description = "the name of the storage access group", + since = "4.21.0") + private String storageAccessGroup; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -112,25 +133,64 @@ public Boolean getShowCapacities() { return showCapacities; } + public CPU.CPUArch getArch() { + return StringUtils.isBlank(arch) ? null : CPU.CPUArch.fromType(arch); + } + + public String getStorageAccessGroup() { + return storageAccessGroup; + } + + public ListClustersCmd() { + + } + + public ListClustersCmd(String storageAccessGroup) { + this.storageAccessGroup = storageAccessGroup; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// + protected void updateClustersExtensions(final List clusterResponses) { + if (CollectionUtils.isEmpty(clusterResponses)) { + return; + } + Map idExtensionMap = new HashMap<>(); + for (ClusterResponse response : clusterResponses) { + if (!Hypervisor.HypervisorType.External.getHypervisorDisplayName().equals(response.getHypervisorType())) { + continue; + } + Long extensionId = extensionHelper.getExtensionIdForCluster(response.getInternalId()); + if (extensionId == null) { + continue; + } + Extension extension = idExtensionMap.computeIfAbsent(extensionId, id -> extensionHelper.getExtension(id)); + if (extension == null) { + continue; + } + response.setExtensionId(extension.getUuid()); + response.setExtensionName(extension.getName()); + } + } + protected Pair, Integer> getClusterResponses() { Pair, Integer> result = _mgr.searchForClusters(this); - List clusterResponses = new ArrayList(); + List clusterResponses = new ArrayList<>(); for (Cluster cluster : result.first()) { ClusterResponse clusterResponse = _responseGenerator.createClusterResponse(cluster, showCapacities); clusterResponse.setObjectName("cluster"); clusterResponses.add(clusterResponse); } - return new Pair, Integer>(clusterResponses, result.second()); + updateClustersExtensions(clusterResponses); + return new Pair<>(clusterResponses, result.second()); } @Override public void execute() { Pair, Integer> clusterResponses = getClusterResponses(); - ListResponse response = new ListResponse(); + ListResponse response = new ListResponse<>(); response.setResponses(clusterResponses.first(), clusterResponses.second()); response.setResponseName(getCommandName()); this.setResponseObject(response); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/UpdateClusterCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/UpdateClusterCmd.java index 77bb97fd39d7..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 @@ -16,6 +16,9 @@ // under the License. package org.apache.cloudstack.api.command.admin.cluster; +import java.util.Map; + +import com.cloud.cpu.CPU; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.APICommand; @@ -29,30 +32,42 @@ import com.cloud.exception.InvalidParameterValueException; import com.cloud.org.Cluster; import com.cloud.user.Account; +import org.apache.commons.lang3.StringUtils; @APICommand(name = "updateCluster", description = "Updates an existing cluster", responseObject = ClusterResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) 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, s390x", + since = "4.20") + private String arch; + + @Parameter(name = ApiConstants.EXTERNAL_DETAILS, + type = CommandType.MAP, + description = "Details in key/value pairs to be added to the extension-resource mapping. Use the format externaldetails[i].=. Example: externaldetails[0].endpoint.url=https://example.com", + since = "4.21.0") + protected Map externalDetails; + public String getClusterName() { return clusterName; } @@ -108,6 +123,17 @@ public ApiCommandResourceType getApiResourceType() { return ApiCommandResourceType.Cluster; } + public CPU.CPUArch getArch() { + if (StringUtils.isBlank(arch)) { + return null; + } + return CPU.CPUArch.fromType(arch); + } + + public Map getExternalDetails() { + return convertDetailsToMap(externalDetails); + } + @Override public void execute() { Cluster cluster = _resourceService.getCluster(getId()); @@ -116,7 +142,7 @@ public void execute() { } Cluster result = _resourceService.updateCluster(this); if (result != null) { - ClusterResponse clusterResponse = _responseGenerator.createClusterResponse(cluster, false); + ClusterResponse clusterResponse = _responseGenerator.createClusterResponse(result, false); clusterResponse.setResponseName(getCommandName()); this.setResponseObject(clusterResponse); } else { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/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..f6f66415f533 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 @@ -52,55 +52,55 @@ 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.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; // /////////////////////////////////////////////////// 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..2d511cff34db 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 @@ -45,43 +45,43 @@ 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; ///////////////////////////////////////////////////// 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..97dee8f638af 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 @@ -45,46 +45,46 @@ 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; @@ -150,7 +150,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 +161,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); } /** 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 50984188bf56..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,22 +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.SECURITY_GROUP_EANBLED, type = CommandType.BOOLEAN, description = "set true to enable security group for this hypervisor.") + @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") + private String hypervisorVersion; + + @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; ///////////////////////////////////////////////////// @@ -73,6 +79,14 @@ public Long getId() { return id; } + public String getHypervisor() { + return hypervisor; + } + + public String getHypervisorVersion() { + return hypervisorVersion; + } + public Long getMaxGuestsLimit() { return maxGuestsLimit; } 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 2098389a1690..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 @@ -20,26 +20,26 @@ import java.util.EnumSet; import java.util.List; -import com.cloud.server.ResourceIcon; -import com.cloud.server.ResourceTag; -import org.apache.cloudstack.api.response.ResourceIconResponse; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.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; import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ResourceIconResponse; +import org.apache.commons.collections.CollectionUtils; import com.cloud.domain.Domain; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.server.ResourceIcon; +import com.cloud.server.ResourceTag; @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,13 +64,16 @@ 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") + private String tag; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -97,7 +100,7 @@ public EnumSet getDetails() throws InvalidParameterValueException dv = EnumSet.of(DomainDetails.all); } else { try { - ArrayList dc = new ArrayList(); + ArrayList dc = new ArrayList<>(); for (String detail : viewDetails) { dc.add(DomainDetails.valueOf(detail)); } @@ -110,10 +113,14 @@ public EnumSet getDetails() throws InvalidParameterValueException return dv; } - public Boolean getShowIcon() { + public boolean getShowIcon() { return showIcon != null ? showIcon : false; } + public String getTag() { + return tag; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -128,12 +135,20 @@ public void execute() { ListResponse response = _queryService.searchForDomains(this); response.setResponseName(getCommandName()); this.setResponseObject(response); - if (response != null && response.getCount() > 0 && getShowIcon()) { - updateDomainResponse(response.getResponses()); - } + updateDomainResponse(response.getResponses()); } - private void updateDomainResponse(List response) { + protected void updateDomainResponse(List response) { + if (CollectionUtils.isEmpty(response)) { + return; + } + EnumSet details = getDetails(); + if (details.contains(DomainDetails.all) || details.contains(DomainDetails.resource)) { + _resourceLimitService.updateTaggedResourceLimitsAndCountsForDomains(response, getTag()); + } + if (!getShowIcon()) { + return; + } for (DomainResponse domainResponse : response) { ResourceIcon resourceIcon = resourceIconManager.getByResourceTypeAndUuid(ResourceTag.ResourceObjectType.Domain, domainResponse.getId()); if (resourceIcon == null) { 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/CreateGpuCardCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuCardCmd.java new file mode 100644 index 000000000000..2faad89bf67e --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuCardCmd.java @@ -0,0 +1,122 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.gpu.GpuCard; + + +@APICommand(name = "createGpuCard", description = "Creates a GPU card definition in the system", + responseObject = GpuCardResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0") +public class CreateGpuCardCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.DEVICE_ID, type = CommandType.STRING, required = true, + description = "the device ID of the GPU card") + private String deviceId; + + @Parameter(name = ApiConstants.DEVICE_NAME, type = CommandType.STRING, required = true, + description = "the device name of the GPU card") + private String deviceName; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, + description = "the display name of the GPU card") + private String name; + + @Parameter(name = ApiConstants.VENDOR_NAME, type = CommandType.STRING, required = true, + description = "the vendor name of the GPU card") + private String vendorName; + + @Parameter(name = ApiConstants.VENDOR_ID, type = CommandType.STRING, required = true, + description = "the vendor ID of the GPU card") + private String vendorId; + + // Optional parameters for the passthrough vGPU profile display properties + @Parameter(name = ApiConstants.VIDEORAM, type = CommandType.LONG, + description = "the video RAM size in MB for the passthrough vGPU profile") + private Long videoRam; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public String getDeviceId() { + return deviceId; + } + + public String getDeviceName() { + return deviceName; + } + + public String getName() { + return name; + } + + public String getVendorName() { + return vendorName; + } + + public String getVendorId() { + return vendorId; + } + + public Long getVideoRam() { + return videoRam; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, + ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try { + GpuCard gpuCard = gpuService.createGpuCard(this); + if (gpuCard != null) { + GpuCardResponse response = new GpuCardResponse(gpuCard); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create GPU card"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create GPU card: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuDeviceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuDeviceCmd.java new file mode 100644 index 000000000000..e6386082a448 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuDeviceCmd.java @@ -0,0 +1,123 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.VgpuProfileResponse; +import org.apache.cloudstack.gpu.GpuDevice; +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.StringUtils; + + +@APICommand(name = "createGpuDevice", description = "Creates a GPU device manually on a host", + responseObject = GpuDeviceResponse.class, since = "4.21.0", requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, authorized = {RoleType.Admin}) +public class CreateGpuDeviceCmd extends BaseCmd { + + @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, required = true, + description = "ID of the host where the GPU device is located") + private Long hostId; + + @Parameter(name = ApiConstants.BUS_ADDRESS, type = CommandType.STRING, required = true, + description = "PCI bus address of the GPU device (e.g., 0000:01:00.0) or UUID for MDEV devices.") + private String busAddress; + + @Parameter(name = ApiConstants.GPU_CARD_ID, type = CommandType.UUID, entityType = GpuCardResponse.class, + required = true, description = "ID of the GPU card type") + private Long gpuCardId; + + @Parameter(name = ApiConstants.VGPU_PROFILE_ID, type = CommandType.UUID, entityType = VgpuProfileResponse.class, + required = true, description = "ID of the vGPU profile") + private Long vgpuProfileId; + + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, + description = "Type of GPU device (PCI, MDEV, VGPUOnly). Defaults to PCI.") + private String type; + + @Parameter(name = ApiConstants.PARENT_GPU_DEVICE_ID, type = CommandType.UUID, entityType = GpuDeviceResponse.class, + description = "ID of the parent GPU device (for virtual GPU devices)") + private Long parentGpuDeviceId; + + @Parameter(name = ApiConstants.NUMA_NODE, type = CommandType.STRING, + description = "NUMA node of the GPU device (e.g., 0, 1, etc.). This is optional and can be used to " + + "specify the NUMA node for the GPU device which is used during allocation. Defaults to -1") + private String numaNode; + + public Long getHostId() { + return hostId; + } + + public String getBusAddress() { + return busAddress; + } + + public Long getGpuCardId() { + return gpuCardId; + } + + public Long getVgpuProfileId() { + return vgpuProfileId; + } + + public GpuDevice.DeviceType getType() { + GpuDevice.DeviceType deviceType = GpuDevice.DeviceType.PCI; + if (StringUtils.isNotBlank(type)) { + deviceType = EnumUtils.getEnumIgnoreCase(GpuDevice.DeviceType.class, type); + if (deviceType == null) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid GPU device type: " + type); + } + } + return deviceType; + } + + public Long getParentGpuDeviceId() { + return parentGpuDeviceId; + } + + public String getNumaNode() { + if (StringUtils.isBlank(numaNode)) { + return "-1"; // Default value for NUMA node + } + return numaNode; + } + + @Override + public void execute() { + try { + GpuDeviceResponse response = gpuService.createGpuDevice(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/CreateVgpuProfileCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/CreateVgpuProfileCmd.java new file mode 100644 index 000000000000..3210773b6f45 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/CreateVgpuProfileCmd.java @@ -0,0 +1,131 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.api.response.VgpuProfileResponse; + + +@APICommand(name = "createVgpuProfile", description = "Creates a vGPU profile in the system", + responseObject = VgpuProfileResponse.class, requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, since = "4.21.0", authorized = {RoleType.Admin}) +public class CreateVgpuProfileCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, + description = "the name of the vGPU profile") + private String name; + + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, + description = "the description of the vGPU profile") + private String description; + + @Parameter(name = ApiConstants.GPU_CARD_ID, type = CommandType.UUID, entityType = GpuCardResponse.class, + required = true, description = "the GPU card ID associated with this GPU device") + private Long cardId; + + @Parameter(name = ApiConstants.MAX_VGPU_PER_PHYSICAL_GPU, type = CommandType.LONG, + description = "Max vGPU per physical GPU. This is used to calculate capacity.") + private Long maxVgpuPerPgpu; + + @Parameter(name = ApiConstants.VIDEORAM, type = CommandType.LONG, + description = "the video RAM size in MB") + private Long videoRam; + + @Parameter(name = ApiConstants.MAXHEADS, type = CommandType.LONG, + description = "the maximum number of display heads") + private Long maxHeads; + + @Parameter(name = ApiConstants.MAXRESOLUTIONX, type = CommandType.LONG, + description = "the maximum X resolution") + private Long maxResolutionX; + + @Parameter(name = ApiConstants.MAXRESOLUTIONY, type = CommandType.LONG, + description = "the maximum Y resolution") + private Long maxResolutionY; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public Long getCardId() { + return cardId; + } + + public Long getMaxVgpuPerPgpu() { + return maxVgpuPerPgpu; + } + + public Long getVideoRam() { + return videoRam; + } + + public Long getMaxHeads() { + return maxHeads; + } + + public Long getMaxResolutionX() { + return maxResolutionX; + } + + public Long getMaxResolutionY() { + return maxResolutionY; + } + + @Override + public void execute() { + try { + VgpuProfileResponse response = gpuService.createVgpuProfile(this); + if (response != null) { + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create vGPU profile"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, + "Failed to create vGPU profile: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuCardCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuCardCmd.java new file mode 100644 index 000000000000..9a510ecdead9 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuCardCmd.java @@ -0,0 +1,75 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.api.response.SuccessResponse; + + +@APICommand(name = "deleteGpuCard", description = "Deletes a GPU card definition from the system", + responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0", authorized = {RoleType.Admin}) +public class DeleteGpuCardCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GpuCardResponse.class, required = true, + description = "the ID of the GPU card") + private Long id; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + @Override + public void execute() { + try { + boolean success = gpuService.deleteGpuCard(this); + if (success) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete GPU card"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete GPU card: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuDeviceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuDeviceCmd.java new file mode 100644 index 000000000000..9224afc66ecf --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuDeviceCmd.java @@ -0,0 +1,78 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.SuccessResponse; + +import java.util.List; + +@APICommand(name = "deleteGpuDevice", description = "Deletes a vGPU profile from the system", + responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0", authorized = {RoleType.Admin}) +public class DeleteGpuDeviceCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.IDS, type = CommandType.LIST, collectionType = CommandType.UUID, + entityType = GpuDeviceResponse.class, required = true, + description = "comma separated list of IDs of the GPU device") + private List ids; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public List getIds() { + return ids; + } + + @Override + public void execute() { + try { + boolean success = gpuService.deleteGpuDevices(this); + if (success) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete vGPU profile"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, + "Failed to delete vGPU profile: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteVgpuProfileCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteVgpuProfileCmd.java new file mode 100644 index 000000000000..a09a04199f57 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteVgpuProfileCmd.java @@ -0,0 +1,76 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.VgpuProfileResponse; + + +@APICommand(name = "deleteVgpuProfile", description = "Deletes a vGPU profile from the system", + responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0", authorized = {RoleType.Admin}) +public class DeleteVgpuProfileCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VgpuProfileResponse.class, required = true, + description = "the ID of the vGPU profile") + private Long id; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + @Override + public void execute() { + try { + boolean success = gpuService.deleteVgpuProfile(this); + if (success) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete vGPU profile"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, + "Failed to delete vGPU profile: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DiscoverGpuDevicesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DiscoverGpuDevicesCmd.java new file mode 100644 index 000000000000..83aca1a2eb64 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DiscoverGpuDevicesCmd.java @@ -0,0 +1,63 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.gpu; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.context.CallContext; + + +@APICommand(name = "discoverGpuDevices", description = "Discovers available GPU devices on a host", + responseObject = GpuDeviceResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0", authorized = {RoleType.Admin}) +public class DiscoverGpuDevicesCmd extends BaseListCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HostResponse.class, required = true, + description = "ID of the host") + private Long id; + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation ////////////////// + /// ////////////////////////////////////////////////// + + @Override + public void execute() { + CallContext.current().setEventDetails("Discovering GPU Devices on host with ID: " + getResourceUuid(ApiConstants.ID)); + ListResponse response = gpuService.discoverGpuDevices(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getId() { + return id; + } + +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/ListGpuDevicesCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/ListGpuDevicesCmdByAdmin.java new file mode 100644 index 000000000000..b3c57713fcd1 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/ListGpuDevicesCmdByAdmin.java @@ -0,0 +1,74 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.gpu; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.command.admin.AdminCmd; +import org.apache.cloudstack.api.command.user.gpu.ListGpuDevicesCmd; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.VgpuProfileResponse; + + +@APICommand(name = "listGpuDevices", description = "Lists all available GPU devices", + responseView = ResponseObject.ResponseView.Full, + responseObject = GpuDeviceResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0") +public class ListGpuDevicesCmdByAdmin extends ListGpuDevicesCmd implements AdminCmd { + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GpuDeviceResponse.class, + description = "ID of the GPU device") + private Long id; + + @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, + description = "the host ID where the GPU device is attached") + private Long hostId; + + @Parameter(name = ApiConstants.GPU_CARD_ID, type = CommandType.UUID, entityType = GpuCardResponse.class, + description = "the GPU card ID associated with the GPU device") + private Long gpuCardId; + + @Parameter(name = ApiConstants.VGPU_PROFILE_ID, type = CommandType.UUID, entityType = VgpuProfileResponse.class, + description = "the vGPU profile ID assigned to the GPU device") + private Long vgpuProfileId; + + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public Long getHostId() { + return hostId; + } + + public Long getGpuCardId() { + return gpuCardId; + } + + public Long getVgpuProfileId() { + return vgpuProfileId; + } + +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/ManageGpuDeviceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/ManageGpuDeviceCmd.java new file mode 100644 index 000000000000..5dfe6c3deee0 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/ManageGpuDeviceCmd.java @@ -0,0 +1,78 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.SuccessResponse; + +import java.util.List; + +@APICommand(name = "manageGpuDevice", description = "Manages a GPU device", responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.21.0", + authorized = {RoleType.Admin}) +public class ManageGpuDeviceCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.IDS, type = CommandType.LIST, collectionType = CommandType.UUID, + entityType = GpuDeviceResponse.class, required = true, + description = "comma separated list of IDs of the GPU device") + private List ids; + + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public List getIds() { + return ids; + } + + @Override + public void execute() { + try { + if (gpuService.enableGpuDevice(this)) { + SuccessResponse response = new SuccessResponse(); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to enable GPU device"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to enable GPU device: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UnmanageGpuDeviceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UnmanageGpuDeviceCmd.java new file mode 100644 index 000000000000..46de23ec44be --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UnmanageGpuDeviceCmd.java @@ -0,0 +1,79 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.SuccessResponse; + +import java.util.List; + +@APICommand(name = "unmanageGpuDevice", description = "Unmanage a GPU device", responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.21.0", + authorized = {RoleType.Admin}) +public class UnmanageGpuDeviceCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.IDS, type = CommandType.LIST, collectionType = CommandType.UUID, + entityType = GpuDeviceResponse.class, required = true, + description = "comma separated list of IDs of the GPU device") + private List ids; + + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public List getIds() { + return ids; + } + + @Override + public void execute() { + try { + if (gpuService.disableGpuDevice(this)) { + SuccessResponse response = new SuccessResponse(); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to disable GPU device"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, + "Failed to disable GPU device: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuCardCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuCardCmd.java new file mode 100644 index 000000000000..0061149a985f --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuCardCmd.java @@ -0,0 +1,99 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.gpu.GpuCard; + + +@APICommand(name = "updateGpuCard", description = "Updates a GPU card definition in the system", + responseObject = GpuCardResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0", authorized = {RoleType.Admin}) +public class UpdateGpuCardCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GpuCardResponse.class, required = true, + description = "the ID of the GPU card") + private Long id; + + @Parameter(name = ApiConstants.DEVICE_NAME, type = CommandType.STRING, + description = "the device name of the GPU card") + private String deviceName; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the display name of the GPU card") + private String name; + + @Parameter(name = ApiConstants.VENDOR_NAME, type = CommandType.STRING, + description = "the vendor name of the GPU card") + private String vendorName; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public String getDeviceName() { + return deviceName; + } + + public String getName() { + return name; + } + + public String getVendorName() { + return vendorName; + } + + @Override + public void execute() { + try { + GpuCard gpuCard = gpuService.updateGpuCard(this); + if (gpuCard != null) { + GpuCardResponse response = new GpuCardResponse(gpuCard); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update GPU card"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update GPU card: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuDeviceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuDeviceCmd.java new file mode 100644 index 000000000000..5ad6e6e1a6f6 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuDeviceCmd.java @@ -0,0 +1,109 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.VgpuProfileResponse; +import org.apache.cloudstack.gpu.GpuDevice; +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.StringUtils; + + +@APICommand(name = "updateGpuDevice", description = "Updates an existing GPU device", + responseObject = GpuDeviceResponse.class, since = "4.21.0", requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, authorized = {RoleType.Admin}) +public class UpdateGpuDeviceCmd extends BaseCmd { + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GpuDeviceResponse.class, required = true, + description = "ID of the GPU device to update") + private Long id; + + @Parameter(name = ApiConstants.GPU_CARD_ID, type = CommandType.UUID, entityType = GpuCardResponse.class, + description = "New GPU card ID") + private Long gpuCardId; + + @Parameter(name = ApiConstants.VGPU_PROFILE_ID, type = CommandType.UUID, entityType = VgpuProfileResponse.class, + description = "New vGPU profile ID") + private Long vgpuProfileId; + + @Parameter(name = "type", type = CommandType.STRING, description = "New type of GPU device (PCI, MDEV, VGPUOnly)") + private String type; + + @Parameter(name = "parentgpudeviceid", type = CommandType.UUID, entityType = GpuDeviceResponse.class, + description = "New parent GPU device ID (for virtual GPU devices)") + private Long parentGpuDeviceId; + + @Parameter(name = ApiConstants.NUMA_NODE, type = CommandType.STRING, + description = "New NUMA node of the GPU device") + private String numaNode; + + public Long getId() { + return id; + } + + public Long getGpuCardId() { + return gpuCardId; + } + + public Long getVgpuProfileId() { + return vgpuProfileId; + } + + public GpuDevice.DeviceType getType() { + GpuDevice.DeviceType deviceType = null; + if (StringUtils.isNotBlank(type)) { + deviceType = EnumUtils.getEnumIgnoreCase(GpuDevice.DeviceType.class, type); + if (deviceType == null) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid GPU device type: " + type); + } + } + return deviceType; + } + + public Long getParentGpuDeviceId() { + return parentGpuDeviceId; + } + + public String getNumaNode() { + return numaNode; + } + + @Override + public void execute() { + try { + GpuDeviceResponse response = gpuService.updateGpuDevice(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateVgpuProfileCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateVgpuProfileCmd.java new file mode 100644 index 000000000000..c8d60739bd45 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateVgpuProfileCmd.java @@ -0,0 +1,129 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.VgpuProfileResponse; + + +@APICommand(name = "updateVgpuProfile", description = "Updates a vGPU profile in the system", + responseObject = VgpuProfileResponse.class, requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, since = "4.21.0", authorized = {RoleType.Admin}) +public class UpdateVgpuProfileCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VgpuProfileResponse.class, required = true, + description = "the ID of the vGPU profile") + private Long id; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the vGPU profile") + private String profileName; + + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, + description = "the description of the vGPU profile") + private String description; + + @Parameter(name = ApiConstants.MAX_VGPU_PER_PHYSICAL_GPU, type = CommandType.LONG, + description = "the maximum number of vGPUs per physical GPU") + private Long maxVgpuPerPgpu; + + @Parameter(name = ApiConstants.VIDEORAM, type = CommandType.LONG, + description = "the video RAM size in MB") + private Long videoRam; + + @Parameter(name = ApiConstants.MAXHEADS, type = CommandType.LONG, + description = "the maximum number of display heads") + private Long maxHeads; + + @Parameter(name = ApiConstants.MAXRESOLUTIONX, type = CommandType.LONG, + description = "the maximum X resolution") + private Long maxResolutionX; + + @Parameter(name = ApiConstants.MAXRESOLUTIONY, type = CommandType.LONG, + description = "the maximum Y resolution") + private Long maxResolutionY; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public String getProfileName() { + return profileName; + } + + public String getDescription() { + return description; + } + + public Long getMaxVgpuPerPgpu() { + return maxVgpuPerPgpu; + } + + public Long getVideoRam() { + return videoRam; + } + + public Long getMaxHeads() { + return maxHeads; + } + + public Long getMaxResolutionX() { + return maxResolutionX; + } + + public Long getMaxResolutionY() { + return maxResolutionY; + } + + @Override + public void execute() { + try { + VgpuProfileResponse response = gpuService.updateVgpuProfile(this); + if (response != null) { + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update vGPU profile"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, + "Failed to update vGPU profile: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCategoryCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCategoryCmd.java new file mode 100644 index 000000000000..f099de43f5d2 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCategoryCmd.java @@ -0,0 +1,93 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.guest; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuestOSCategoryResponse; + +import com.cloud.storage.GuestOsCategory; +import com.cloud.user.Account; + +@APICommand(name = "addOsCategory", + description = "Adds a new OS category", + responseObject = GuestOSCategoryResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + since = "4.21.0", + authorized = {RoleType.Admin}) +public class AddGuestOsCategoryCmd extends BaseCmd { + + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "Name of the OS category", + required = true) + private String name; + + @Parameter(name = ApiConstants.IS_FEATURED, type = CommandType.BOOLEAN, + description = "Whether the category is featured or not") + private Boolean featured; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getName() { + return name; + } + + public boolean isFeatured() { + return Boolean.TRUE.equals(featured); + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + GuestOsCategory guestOs = _mgr.addGuestOsCategory(this); + if (guestOs != null) { + GuestOSCategoryResponse response = _responseGenerator.createGuestOSCategoryResponse(guestOs); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add new OS category"); + } + + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.GuestOsCategory; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/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/DeleteGuestOsCategoryCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/DeleteGuestOsCategoryCmd.java new file mode 100644 index 000000000000..8cf62ee2aabb --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/DeleteGuestOsCategoryCmd.java @@ -0,0 +1,89 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.guest; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuestOSCategoryResponse; +import org.apache.cloudstack.api.response.SuccessResponse; + +import com.cloud.user.Account; + + +@APICommand(name = "deleteOsCategory", + description = "Deletes an OS category", + responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + since = "4.21.0", + authorized = {RoleType.Admin}) +public class DeleteGuestOsCategoryCmd extends BaseCmd { + + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOSCategoryResponse.class, + required = true, description = "ID of the OS category") + private Long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + boolean result = _mgr.deleteGuestOsCategory(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove OS category"); + } + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.GuestOsCategory; + } + + @Override + public Long getApiResourceId() { + return getId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/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/UpdateGuestOsCategoryCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCategoryCmd.java new file mode 100644 index 000000000000..4041967abe8a --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCategoryCmd.java @@ -0,0 +1,113 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.guest; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuestOSCategoryResponse; + +import com.cloud.storage.GuestOsCategory; +import com.cloud.user.Account; + +@APICommand(name = "updateOsCategory", + description = "Updates an OS category", + responseObject = GuestOSCategoryResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + since = "4.21.0", + authorized = {RoleType.Admin}) +public class UpdateGuestOsCategoryCmd extends BaseCmd { + + + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOSCategoryResponse.class, + required = true, description = "ID of the OS category") + private Long id; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "Name for the OS category") + private String name; + + @Parameter(name = ApiConstants.IS_FEATURED, type = CommandType.BOOLEAN, + description = "Whether the category is featured or not") + private Boolean featured; + + @Parameter(name = ApiConstants.SORT_KEY, type = CommandType.INTEGER, + description = "sort key of the OS category for listing") + private Integer sortKey; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public Boolean isFeatured() { + return featured; + } + + public Integer getSortKey() { + return sortKey; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute() { + GuestOsCategory guestOs = _mgr.updateGuestOsCategory(this); + if (guestOs != null) { + GuestOSCategoryResponse response = _responseGenerator.createGuestOSCategoryResponse(guestOs); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update OS category"); + } + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.GuestOsCategory; + } + + @Override + public Long getApiResourceId() { + return getId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmd.java index c98cd149ef30..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 @@ -16,8 +16,12 @@ // under the License. package org.apache.cloudstack.api.command.admin.guest; -import org.apache.commons.collections.MapUtils; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -25,18 +29,14 @@ import org.apache.cloudstack.api.BaseAsyncCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuestOSCategoryResponse; import org.apache.cloudstack.api.response.GuestOSResponse; -import org.apache.cloudstack.acl.RoleType; +import org.apache.commons.collections.MapUtils; import com.cloud.event.EventTypes; import com.cloud.storage.GuestOS; import com.cloud.user.Account; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - @APICommand(name = "updateGuestOs", description = "Updates the information about Guest OS", responseObject = GuestOSResponse.class, since = "4.4.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class UpdateGuestOsCmd extends BaseAsyncCmd { @@ -50,7 +50,7 @@ public class UpdateGuestOsCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOSResponse.class, required = true, description = "UUID of the Guest OS") private Long id; - @Parameter(name = ApiConstants.OS_DISPLAY_NAME, type = CommandType.STRING, required = true, description = "Unique display name for Guest OS") + @Parameter(name = ApiConstants.OS_DISPLAY_NAME, type = CommandType.STRING, description = "Unique display name for Guest OS") private String osDisplayName; @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, required = false, description = "Map of (key/value pairs)") @@ -59,6 +59,12 @@ public class UpdateGuestOsCmd extends BaseAsyncCmd { @Parameter(name="forDisplay", type=CommandType.BOOLEAN, description="whether this guest OS is available for end users", authorized = {RoleType.Admin}) private Boolean display; + @Parameter(name = ApiConstants.OS_CATEGORY_ID, type = CommandType.UUID, entityType = GuestOSCategoryResponse.class, + description = "the ID of the OS category", since = "4.21.0") + private Long osCategoryId; + + + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -71,9 +77,9 @@ public String getOsDisplayName() { return osDisplayName; } - public Map getDetails() { - Map detailsMap = new HashMap<>();; - if (MapUtils.isNotEmpty(detailsMap)) { + public Map getDetails() { + Map detailsMap = new HashMap<>(); + if (MapUtils.isNotEmpty(details)) { Collection servicesCollection = details.values(); Iterator iter = servicesCollection.iterator(); while (iter.hasNext()) { @@ -90,6 +96,10 @@ public Boolean getForDisplay() { return display; } + public Long getOsCategoryId() { + return osCategoryId; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -113,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..6804e4355ca8 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 @@ -102,7 +102,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 +115,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 ca27837aa881..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 @@ -18,7 +18,7 @@ import java.util.ArrayList; import java.util.List; - +import java.util.Map; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -45,36 +45,49 @@ 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, + type = CommandType.LIST, collectionType = CommandType.STRING, + description = "comma separated list of storage access groups for the host", + since = "4.21.0") + private List storageAccessGroups; + + @Parameter(name = ApiConstants.EXTERNAL_DETAILS, + type = CommandType.MAP, + description = "Details in key/value pairs using format externaldetails[i].keyname=keyvalue. Example: externaldetails[0].endpoint.url=urlvalue", + since = "4.21.0") + protected Map externalDetails; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -115,10 +128,18 @@ public List getHostTags() { return hostTags; } + public List getStorageAccessGroups() { + return storageAccessGroups; + } + public String getAllocationState() { return allocationState; } + public Map getExternalDetails() { + return convertExternalDetailsToMap(externalDetails); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/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 new file mode 100644 index 000000000000..5d44bafb4b5c --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/CancelHostMaintenanceCmd.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 +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.host; + + +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.context.CallContext; + +import com.cloud.event.EventTypes; +import com.cloud.host.Host; +import com.cloud.user.Account; + +@APICommand(name = "cancelHostMaintenance", description = "Cancels host maintenance.", responseObject = HostResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class CancelHostMaintenanceCmd extends BaseAsyncCmd { + + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HostResponse.class, required = true, description = "The host ID") + private Long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + public static String getResultObjectName() { + return "host"; + } + + @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_MAINTENANCE_CANCEL; + } + + @Override + public String getEventDescription() { + return "Canceling maintenance for host with ID: " + getResourceUuid(ApiConstants.ID); + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.Host; + } + + @Override + public Long getApiResourceId() { + return getId(); + } + + @Override + public void execute() { + Host result = _resourceService.cancelMaintenance(this); + if (result != null) { + HostResponse response = _responseGenerator.createHostResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to cancel host maintenance"); + } + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/CancelMaintenanceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/CancelMaintenanceCmd.java deleted file mode 100644 index a514a61b8a41..000000000000 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/CancelMaintenanceCmd.java +++ /dev/null @@ -1,103 +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.host; - - -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.context.CallContext; - -import com.cloud.event.EventTypes; -import com.cloud.host.Host; -import com.cloud.user.Account; - -@APICommand(name = "cancelHostMaintenance", description = "Cancels host maintenance.", responseObject = HostResponse.class, - requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) -public class CancelMaintenanceCmd extends BaseAsyncCmd { - - - ///////////////////////////////////////////////////// - //////////////// API parameters ///////////////////// - ///////////////////////////////////////////////////// - - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HostResponse.class, required = true, description = "the host ID") - private Long id; - - ///////////////////////////////////////////////////// - /////////////////// Accessors /////////////////////// - ///////////////////////////////////////////////////// - - public Long getId() { - return id; - } - - ///////////////////////////////////////////////////// - /////////////// API Implementation/////////////////// - ///////////////////////////////////////////////////// - - public static String getResultObjectName() { - return "host"; - } - - @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_MAINTENANCE_CANCEL; - } - - @Override - public String getEventDescription() { - return "canceling maintenance for host: " + getId(); - } - - @Override - public ApiCommandResourceType getApiResourceType() { - return ApiCommandResourceType.Host; - } - - @Override - public Long getApiResourceId() { - return getId(); - } - - @Override - public void execute() { - Host result = _resourceService.cancelMaintenance(this); - if (result != null) { - HostResponse response = _responseGenerator.createHostResponse(result); - response.setResponseName(getCommandName()); - this.setResponseObject(response); - } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to cancel host maintenance"); - } - } -} 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..abca619f82a7 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; ///////////////////////////////////////////////////// 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 af87bbf33bb0..e202dfad77ba 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/ListHostsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/ListHostsCmd.java @@ -21,7 +21,6 @@ import java.util.List; import java.util.Map; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -31,10 +30,13 @@ import org.apache.cloudstack.api.response.ClusterResponse; import org.apache.cloudstack.api.response.HostResponse; import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ManagementServerResponse; import org.apache.cloudstack.api.response.PodResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.commons.lang3.StringUtils; +import com.cloud.cpu.CPU; import com.cloud.exception.InvalidParameterValueException; import com.cloud.host.Host; import com.cloud.hypervisor.Hypervisor.HypervisorType; @@ -50,61 +52,75 @@ 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") + private Long managementServerId; + + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, description = "CPU Arch of the host", since = "4.20.1") + private String arch; + + @Parameter(name = ApiConstants.STORAGE_ACCESS_GROUP, type = CommandType.STRING, + description = "the name of the storage access group", + since = "4.21.0") + private String storageAccessGroup; + + @Parameter(name = ApiConstants.VERSION, type = CommandType.STRING, description = "the host version", since = "4.20.3") + private String version; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -189,6 +205,30 @@ public String getHostOutOfBandManagementPowerState() { return outOfBandManagementPowerState; } + public Long getManagementServerId() { + return managementServerId; + } + + public CPU.CPUArch getArch() { + return StringUtils.isBlank(arch) ? null : CPU.CPUArch.fromType(arch); + } + + public String getStorageAccessGroup() { + return storageAccessGroup; + } + + public ListHostsCmd() { + + } + + public ListHostsCmd(String storageAccessGroup) { + this.storageAccessGroup = storageAccessGroup; + } + + public String getVersion() { + return version; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// 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 new file mode 100644 index 000000000000..843c7fd7fcbe --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/PrepareForHostMaintenanceCmd.java @@ -0,0 +1,111 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.host; + +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.context.CallContext; + +import com.cloud.event.EventTypes; +import com.cloud.host.Host; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "prepareHostForMaintenance", description = "Prepares a host for maintenance.", responseObject = HostResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class PrepareForHostMaintenanceCmd extends BaseAsyncCmd { + + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HostResponse.class, required = true, description = "The host ID") + private Long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + public static String getResultObjectName() { + return "host"; + } + + @Override + public long getEntityOwnerId() { + Account account = CallContext.current().getCallingAccount(); + if (account != null) { + return account.getId(); + } + + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } + + @Override + public String getEventType() { + return EventTypes.EVENT_MAINTENANCE_PREPARE; + } + + @Override + public String getEventDescription() { + return "Preparing host with ID: " + getResourceUuid(ApiConstants.ID) + " for maintenance."; + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.Host; + } + + @Override + public Long getApiResourceId() { + return getId(); + } + + public void setId(Long id) { + this.id = id; + } + + @Override + public void execute() { + try { + Host result = _resourceService.maintain(this); + if (result != null) { + HostResponse response = _responseGenerator.createHostResponse(result); + response.setResponseName("host"); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to prepare host for maintenance"); + } + } catch (CloudRuntimeException exception) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to prepare host for maintenance due to: " + exception.getMessage()); + } + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/PrepareForMaintenanceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/PrepareForMaintenanceCmd.java deleted file mode 100644 index 2641c54364ee..000000000000 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/PrepareForMaintenanceCmd.java +++ /dev/null @@ -1,111 +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.host; - -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.context.CallContext; - -import com.cloud.event.EventTypes; -import com.cloud.host.Host; -import com.cloud.user.Account; -import com.cloud.utils.exception.CloudRuntimeException; - -@APICommand(name = "prepareHostForMaintenance", description = "Prepares a host for maintenance.", responseObject = HostResponse.class, - requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) -public class PrepareForMaintenanceCmd extends BaseAsyncCmd { - - - ///////////////////////////////////////////////////// - //////////////// API parameters ///////////////////// - ///////////////////////////////////////////////////// - - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HostResponse.class, required = true, description = "the host ID") - private Long id; - - ///////////////////////////////////////////////////// - /////////////////// Accessors /////////////////////// - ///////////////////////////////////////////////////// - - public Long getId() { - return id; - } - - ///////////////////////////////////////////////////// - /////////////// API Implementation/////////////////// - ///////////////////////////////////////////////////// - - public static String getResultObjectName() { - return "host"; - } - - @Override - public long getEntityOwnerId() { - Account account = CallContext.current().getCallingAccount(); - if (account != null) { - return account.getId(); - } - - return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked - } - - @Override - public String getEventType() { - return EventTypes.EVENT_MAINTENANCE_PREPARE; - } - - @Override - public String getEventDescription() { - return "preparing host: " + getId() + " for maintenance"; - } - - @Override - public ApiCommandResourceType getApiResourceType() { - return ApiCommandResourceType.Host; - } - - @Override - public Long getApiResourceId() { - return getId(); - } - - public void setId(Long id) { - this.id = id; - } - - @Override - public void execute() { - try { - Host result = _resourceService.maintain(this); - if (result != null) { - HostResponse response = _responseGenerator.createHostResponse(result); - response.setResponseName("host"); - this.setResponseObject(response); - } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to prepare host for maintenance"); - } - } catch (CloudRuntimeException exception) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to prepare host for maintenance due to: " + exception.getMessage()); - } - } -} 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 88eeadb9b139..c085abd42c76 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/UpdateHostCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/UpdateHostCmd.java @@ -16,8 +16,9 @@ // under the License. package org.apache.cloudstack.api.command.admin.host; -import com.cloud.host.Host; -import com.cloud.user.Account; +import java.util.List; +import java.util.Map; + import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -28,7 +29,8 @@ import org.apache.cloudstack.api.response.GuestOSCategoryResponse; import org.apache.cloudstack.api.response.HostResponse; -import java.util.List; +import com.cloud.host.Host; +import com.cloud.user.Account; @APICommand(name = "updateHost", description = "Updates a host.", responseObject = HostResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -38,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}) @@ -47,7 +49,7 @@ 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 to update the host with") private Long osCategoryId; @Parameter(name = ApiConstants.ALLOCATION_STATE, @@ -55,18 +57,29 @@ public class UpdateHostCmd extends BaseCmd { 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}) private String annotation; + @Parameter(name = ApiConstants.EXTERNAL_DETAILS, type = CommandType.MAP, description = "Details in key/value pairs using format externaldetails[i].keyname=keyvalue. Example: externaldetails[0].endpoint.url=urlvalue", since = "4.21.0") + protected Map externalDetails; + + @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 /////////////////////// ///////////////////////////////////////////////////// @@ -103,6 +116,14 @@ public String getAnnotation() { return annotation; } + public Map getExternalDetails() { + return convertExternalDetailsToMap(externalDetails); + } + + public boolean isCleanupExternalDetails() { + return Boolean.TRUE.equals(cleanupExternalDetails); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -125,8 +146,9 @@ public void execute() { hostResponse.setResponseName(getCommandName()); this.setResponseObject(hostResponse); } catch (Exception e) { - logger.debug("Failed to update host:" + getId(), e); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update host:" + getId() + "," + e.getMessage()); + Host host = _entityMgr.findById(Host.class, getId()); + 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 18dfc87397ae..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 @@ -17,11 +17,6 @@ package org.apache.cloudstack.api.command.admin.internallb; -import java.util.List; - -import javax.inject.Inject; - - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; @@ -47,9 +42,6 @@ responseHasSensitiveInfo = false) public class ConfigureInternalLoadBalancerElementCmd extends BaseAsyncCmd { - @Inject - private List _service; - ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @@ -58,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") @@ -92,13 +84,14 @@ 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); - VirtualRouterProvider result = _service.get(0).configureInternalLoadBalancerElement(getId(), getEnabled()); + CallContext.current().setEventDetails("Internal load balancer element: " + getResourceUuid(ApiConstants.ID)); + InternalLoadBalancerElementService service = _networkService.getInternalLoadBalancerElementById(id); + VirtualRouterProvider result = service.configureInternalLoadBalancerElement(getId(), getEnabled()); if (result != null) { InternalLoadBalancerElementResponse routerResponse = _responseGenerator.createInternalLbElementResponse(result); routerResponse.setResponseName(getCommandName()); 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 971f097fca52..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 @@ -16,11 +16,6 @@ // under the License. package org.apache.cloudstack.api.command.admin.internallb; -import java.util.List; - -import javax.inject.Inject; - - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; @@ -45,9 +40,6 @@ responseHasSensitiveInfo = false) public class CreateInternalLoadBalancerElementCmd extends BaseAsyncCreateCmd { - @Inject - private List _service; - ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @@ -56,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; ///////////////////////////////////////////////////// @@ -82,8 +74,9 @@ public long getEntityOwnerId() { @Override public void execute() { - CallContext.current().setEventDetails("Virtual router element Id: " + getEntityId()); - VirtualRouterProvider result = _service.get(0).getInternalLoadBalancerElement(getEntityId()); + CallContext.current().setEventDetails("Virtual router element ID: " + getEntityUuid()); + InternalLoadBalancerElementService service = _networkService.getInternalLoadBalancerElementByNetworkServiceProviderId(getNspId()); + VirtualRouterProvider result = service.getInternalLoadBalancerElement(getEntityId()); if (result != null) { InternalLoadBalancerElementResponse response = _responseGenerator.createInternalLbElementResponse(result); response.setResponseName(getCommandName()); @@ -95,7 +88,8 @@ public void execute() { @Override public void create() throws ResourceAllocationException { - VirtualRouterProvider result = _service.get(0).addInternalLoadBalancerElement(getNspId()); + InternalLoadBalancerElementService service = _networkService.getInternalLoadBalancerElementByNetworkServiceProviderId(getNspId()); + VirtualRouterProvider result = service.addInternalLoadBalancerElement(getNspId()); if (result != null) { setEntityId(result.getId()); setEntityUuid(result.getUuid()); 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 6c2fadee7370..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 @@ -17,11 +17,9 @@ package org.apache.cloudstack.api.command.admin.internallb; import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import javax.inject.Inject; - - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseListCmd; @@ -46,25 +44,22 @@ responseHasSensitiveInfo = false) public class ListInternalLoadBalancerElementsCmd extends BaseListCmd { - @Inject - private InternalLoadBalancerElementService _service; - ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @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; ///////////////////////////////////////////////////// @@ -86,12 +81,21 @@ public Boolean getEnabled() { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { - List providers = _service.searchForInternalLoadBalancerElements(getId(), getNspId(), getEnabled()); + List services; + if (id == null && nspId == null) { + services = _networkService.getInternalLoadBalancerElements(); + } else { + InternalLoadBalancerElementService elementService = id != null ? _networkService.getInternalLoadBalancerElementById(id) : _networkService.getInternalLoadBalancerElementByNetworkServiceProviderId(nspId); + services = Collections.singletonList(elementService); + } ListResponse response = new ListResponse(); List providerResponses = new ArrayList(); - for (VirtualRouterProvider provider : providers) { - InternalLoadBalancerElementResponse providerResponse = _responseGenerator.createInternalLbElementResponse(provider); - providerResponses.add(providerResponse); + for (InternalLoadBalancerElementService service : services) { + List providers = service.searchForInternalLoadBalancerElements(getId(), getNspId(), getEnabled()); + for (VirtualRouterProvider provider : providers) { + InternalLoadBalancerElementResponse providerResponse = _responseGenerator.createInternalLbElementResponse(provider); + providerResponses.add(providerResponse); + } } response.setResponses(providerResponses); response.setResponseName(getCommandName()); 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/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 a68ed62857ac..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 @@ -23,6 +23,7 @@ import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.ManagementServerResponse; +import org.apache.commons.lang3.BooleanUtils; @APICommand(name = "listManagementServers", description = "Lists management servers.", responseObject = ManagementServerResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -33,12 +34,21 @@ 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, + description = "Whether to return the management server peers or not. By default, the management server peers will not be returned.", + 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 /////////////////////// ///////////////////////////////////////////////////// @@ -51,6 +61,14 @@ public String getHostName() { return hostName; } + 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/management/RemoveManagementServerCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/management/RemoveManagementServerCmd.java new file mode 100644 index 000000000000..803b95bfa9e2 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/management/RemoveManagementServerCmd.java @@ -0,0 +1,61 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.management; + +import com.cloud.event.EventTypes; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.response.ManagementServerResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; + +@APICommand(name = "removeManagementServer", description = "Removes a Management Server.", responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = RoleType.Admin) +public class RemoveManagementServerCmd extends BaseCmd { + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ManagementServerResponse.class, required = true, description = "the ID of the Management Server") + private Long id; + + public Long getId() { + return id; + } + + @Override + public void execute() { + boolean result = _mgr.removeManagementServer(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove Management Server."); + } + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } + + public String getEventType() { + return EventTypes.EVENT_MANAGEMENT_SERVER_REMOVE; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/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/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 new file mode 100644 index 000000000000..4d645376a909 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForGuestNetworkCmd.java @@ -0,0 +1,108 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.network; + +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.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.api.response.Ipv4SubnetForGuestNetworkResponse; + +import com.cloud.event.EventTypes; +import com.cloud.user.Account; +import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap; + +@APICommand(name = "createIpv4SubnetForGuestNetwork", + description = "Creates a IPv4 subnet for guest networks.", + responseObject = Ipv4SubnetForGuestNetworkResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class CreateIpv4SubnetForGuestNetworkCmd extends BaseAsyncCmd { + + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name = ApiConstants.PARENT_ID, + type = CommandType.UUID, + entityType = DataCenterIpv4SubnetResponse.class, + required = true, + description = "The zone Ipv4 subnet which the IPv4 subnet belongs to.") + private Long parentId; + + @Parameter(name = ApiConstants.SUBNET, + type = CommandType.STRING, + description = "The CIDR of this Ipv4 subnet.") + private String subnet; + + @Parameter(name = ApiConstants.CIDR_SIZE, + type = CommandType.INTEGER, + description = "the CIDR size of IPv4 network. This is mutually exclusive with subnet.") + private Integer cidrSize; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public Long getParentId() { + return parentId; + } + + public String getSubnet() { + return subnet; + } + + public Integer getCidrSize() { + return cidrSize; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_IP4_GUEST_SUBNET_CREATE; + } + + @Override + public String getEventDescription() { + return "Creating guest IPv4 subnet " + getSubnet() + " in zone subnet: " + getResourceUuid(ApiConstants.PARENT_ID); + } + + @Override + public void execute() { + Ipv4GuestSubnetNetworkMap result = routedIpv4Manager.createIpv4SubnetForGuestNetwork(this); + if (result != null) { + Ipv4SubnetForGuestNetworkResponse response = routedIpv4Manager.createIpv4SubnetForGuestNetworkResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create zone guest IPv4 subnet."); + } + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + +} 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 new file mode 100644 index 000000000000..48a6002fb5c0 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForZoneCmd.java @@ -0,0 +1,125 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiArgValidator; +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.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; + +import com.cloud.event.EventTypes; +import com.cloud.user.Account; + +@APICommand(name = "createIpv4SubnetForZone", + description = "Creates a IPv4 subnet for a zone.", + responseObject = DataCenterIpv4SubnetResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class CreateIpv4SubnetForZoneCmd extends BaseAsyncCmd { + + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name = ApiConstants.ZONE_ID, + type = CommandType.UUID, + entityType = ZoneResponse.class, + required = true, + description = "UUID of the zone which the IPv4 subnet belongs to.", + validations = {ApiArgValidator.PositiveNumber}) + private Long zoneId; + + @Parameter(name = ApiConstants.SUBNET, + type = CommandType.STRING, + required = true, + description = "The CIDR of the IPv4 subnet.") + private String subnet; + + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "account who will own the IPv4 subnet") + private String accountName; + + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "project who will own the IPv4 subnet") + private Long projectId; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "domain ID of the account owning the IPv4 subnet") + private Long domainId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public Long getZoneId() { + return zoneId; + } + + public String getSubnet() { + return subnet; + } + + public String getAccountName() { + return accountName; + } + + public Long getProjectId() { + return projectId; + } + + public Long getDomainId() { + return domainId; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_ZONE_IP4_SUBNET_CREATE; + } + + @Override + public String getEventDescription() { + return "Creating guest IPv4 subnet " + getSubnet() + " for zone: " + getResourceUuid(ApiConstants.ZONE_ID); + } + + @Override + public void execute() { + DataCenterIpv4GuestSubnet result = routedIpv4Manager.createDataCenterIpv4GuestSubnet(this); + if (result != null) { + DataCenterIpv4SubnetResponse response = routedIpv4Manager.createDataCenterIpv4SubnetResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create zone guest IPv4 subnet."); + } + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + +} 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 cd9770877ed7..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 @@ -24,31 +24,42 @@ import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.command.admin.AdminCmd; import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd; +import org.apache.cloudstack.api.response.BgpPeerResponse; import org.apache.cloudstack.api.response.NetworkResponse; import com.cloud.network.Network; -@APICommand(name = "createNetwork", description = "Creates a network", responseObject = NetworkResponse.class, responseView = ResponseView.Full, entityType = {Network.class}, +import java.util.List; + +@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; + @Parameter(name = ApiConstants.BGP_PEER_IDS, + type = CommandType.LIST, + collectionType = CommandType.UUID, + entityType = BgpPeerResponse.class, + description = "IDs of the Bgp Peer for the Network", + since = "4.20.0") + private List bgpPeerIds; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -78,4 +89,8 @@ public String getRouterIp() { public String getRouterIpv6() { return routerIpv6; } + + public List getBgpPeerIds() { + return 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 f2b1a18831a0..a0559f57dab0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.command.admin.network; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; @@ -24,10 +25,14 @@ 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; @@ -46,6 +51,20 @@ 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; + +import static org.apache.cloudstack.api.command.utils.OfferingUtils.isNetrisNatted; +import static org.apache.cloudstack.api.command.utils.OfferingUtils.isNetrisRouted; +import static org.apache.cloudstack.api.command.utils.OfferingUtils.isNsxWithoutLb; + @APICommand(name = "createNetworkOffering", description = "Creates a network offering.", responseObject = NetworkOfferingResponse.class, since = "3.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateNetworkOfferingCmd extends BaseCmd { @@ -54,81 +73,112 @@ public class CreateNetworkOfferingCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "the name of the network offering") + @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'.") + @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) + @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") + @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. " + @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") + @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") + @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") + 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", + 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") + 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") + 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") + @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") + 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") + 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.") + 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.") + 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." @@ -138,41 +188,51 @@ public class CreateNetworkOfferingCmd extends BaseCmd { @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") + 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.") + 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.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.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; + @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 /////////////////////// ///////////////////////////////////////////////////// @@ -209,8 +269,48 @@ 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() { - return supportedServices == null ? new ArrayList() : supportedServices; + 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 getGuestIpType() { @@ -240,6 +340,18 @@ 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; } @@ -260,9 +372,8 @@ public Integer getMaxconnections() { } public Map> getServiceProviders() { - Map> serviceProviderMap = null; - if (serviceProviderList != null && !serviceProviderList.isEmpty()) { - serviceProviderMap = new HashMap>(); + Map> serviceProviderMap = new HashMap<>(); + if (serviceProviderList != null && !serviceProviderList.isEmpty() && !isExternalNetworkProvider()) { Collection servicesCollection = serviceProviderList.values(); Iterator iter = servicesCollection.iterator(); while (iter.hasNext()) { @@ -278,11 +389,37 @@ public Map> getServiceProviders() { 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 (isNsxWithoutLb(getProvider(), getNsxSupportsLbService()) || isNetrisRouted(getProvider(), getNetworkMode())) { + serviceProviderMap.remove(Lb.getName()); + } + } + } + public Map getServiceCapabilities(Service service) { Map capabilityMap = null; @@ -363,6 +500,14 @@ public Boolean getEnable() { return false; } + public boolean getSpecifyAsNumber() { + return BooleanUtils.toBoolean(specifyAsNumber); + } + + public String getRoutingMode() { + return routingMode; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// 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 new file mode 100644 index 000000000000..cc76b284e24a --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DedicateIpv4SubnetForZoneCmd.java @@ -0,0 +1,111 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 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.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "dedicateIpv4SubnetForZone", + description = "Dedicates an existing IPv4 subnet for a zone to an account or a domain.", + responseObject = DataCenterIpv4SubnetResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class DedicateIpv4SubnetForZoneCmd extends BaseAsyncCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DataCenterIpv4SubnetResponse.class, required = true, description = "Id of the guest network IPv4 subnet") + private Long id; + + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "account who will own the IPv4 subnet") + private String accountName; + + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "project who will own the IPv4 subnet") + private Long projectId; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "domain ID of the account owning the IPv4 subnet") + private Long domainId; + + public Long getId() { + return id; + } + + public String getAccountName() { + return accountName; + } + + public Long getProjectId() { + return projectId; + } + + public Long getDomainId() { + return domainId; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_ZONE_IP4_SUBNET_DEDICATE; + } + + @Override + public String getEventDescription() { + return "Dedicating zone's IPv4 subnet with ID: " + getResourceUuid(ApiConstants.ID); + } + + @Override + public void execute() { + try { + DataCenterIpv4GuestSubnet result = routedIpv4Manager.dedicateDataCenterIpv4GuestSubnet(this); + if (result != null) { + DataCenterIpv4SubnetResponse response = routedIpv4Manager.createDataCenterIpv4SubnetResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to dedicate guest network IPv4 subnet:" + getId()); + } + } catch (InvalidParameterValueException ex) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage()); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} 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 new file mode 100644 index 000000000000..f6b22f79dfc7 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForGuestNetworkCmd.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.admin.network; + +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.response.Ipv4SubnetForGuestNetworkResponse; +import org.apache.cloudstack.api.response.SuccessResponse; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "deleteIpv4SubnetForGuestNetwork", + description = "Deletes an existing IPv4 subnet for guest network.", + responseObject = SuccessResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class DeleteIpv4SubnetForGuestNetworkCmd extends BaseAsyncCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = Ipv4SubnetForGuestNetworkResponse.class, required = true, description = "Id of the guest network IPv4 subnet") + private Long id; + + public Long getId() { + return id; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_IP4_GUEST_SUBNET_DELETE; + } + + @Override + public String getEventDescription() { + return "Deleting guest IPv4 subnet with ID: " + getResourceUuid(ApiConstants.ID); + } + + @Override + public void execute() { + try { + boolean result = routedIpv4Manager.deleteIpv4SubnetForGuestNetwork(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete guest network IPv4 subnet:" + getId()); + } + } catch (InvalidParameterValueException ex) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage()); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} 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 new file mode 100644 index 000000000000..0ff2a9ad70b8 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForZoneCmd.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.admin.network; + +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.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.api.response.SuccessResponse; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "deleteIpv4SubnetForZone", + description = "Deletes an existing IPv4 subnet for a zone.", + responseObject = SuccessResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class DeleteIpv4SubnetForZoneCmd extends BaseAsyncCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DataCenterIpv4SubnetResponse.class, required = true, description = "Id of the guest network IPv4 subnet") + private Long id; + + public Long getId() { + return id; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_ZONE_IP4_SUBNET_DELETE; + } + + @Override + public String getEventDescription() { + return "Deleting zone IPv4 subnet with ID: " + getResourceUuid(ApiConstants.ID); + } + + @Override + public void execute() { + try { + boolean result = routedIpv4Manager.deleteDataCenterIpv4GuestSubnet(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete guest network IPv4 subnet:" + getId()); + } + } catch (InvalidParameterValueException ex) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage()); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} 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/ListIpv4SubnetsForGuestNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListIpv4SubnetsForGuestNetworkCmd.java new file mode 100644 index 000000000000..9761f6e89ebc --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListIpv4SubnetsForGuestNetworkCmd.java @@ -0,0 +1,123 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.network; + +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.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.api.response.Ipv4SubnetForGuestNetworkResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.api.response.VpcResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap; + +@APICommand(name = "listIpv4SubnetsForGuestNetwork", + description = "Lists IPv4 subnets for guest networks.", + responseObject = Ipv4SubnetForGuestNetworkResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class ListIpv4SubnetsForGuestNetworkCmd extends BaseListCmd { + + @Parameter(name = ApiConstants.ID, + type = CommandType.UUID, + entityType = Ipv4SubnetForGuestNetworkResponse.class, + description = "UUID of the IPv4 subnet for guest network.") + private Long id; + + @Parameter(name = ApiConstants.PARENT_ID, + type = CommandType.UUID, + entityType = DataCenterIpv4SubnetResponse.class, + description = "UUID of zone Ipv4 subnet which the IPv4 subnet belongs to.") + private Long parentId; + + @Parameter(name = ApiConstants.SUBNET, + type = CommandType.STRING, + description = "The CIDR of the Ipv4 subnet.") + private String subnet; + + @Parameter(name = ApiConstants.ZONE_ID, + type = CommandType.UUID, + entityType = ZoneResponse.class, + description = "UUID of zone to which the IPv4 subnet belongs to.") + private Long zoneId; + + @Parameter(name = ApiConstants.NETWORK_ID, + type = CommandType.UUID, + entityType = NetworkResponse.class, + description = "UUID of network to which the IPv4 subnet is associated to.") + private Long networkId; + + @Parameter(name = ApiConstants.VPC_ID, + type = CommandType.UUID, + entityType = VpcResponse.class, + description = "UUID of VPC to which the IPv4 subnet is associated to.") + private Long vpcId; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public Long getParentId() { + return parentId; + } + + public Long getZoneId() { + return zoneId; + } + + public String getSubnet() { + return subnet; + } + + public Long getNetworkId() { + return networkId; + } + + public Long getVpcId() { + return vpcId; + } + + @Override + public void execute() { + List subnets = routedIpv4Manager.listIpv4GuestSubnetsForGuestNetwork(this); + ListResponse response = new ListResponse<>(); + List subnetResponses = new ArrayList<>(); + for (Ipv4GuestSubnetNetworkMap subnet : subnets) { + Ipv4SubnetForGuestNetworkResponse subnetResponse = routedIpv4Manager.createIpv4SubnetForGuestNetworkResponse(subnet); + subnetResponses.add(subnetResponse); + } + + response.setResponses(subnetResponses, subnets.size()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } + +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListIpv4SubnetsForZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListIpv4SubnetsForZoneCmd.java new file mode 100644 index 000000000000..2c2182250edb --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListIpv4SubnetsForZoneCmd.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 +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.ArrayList; +import java.util.List; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; + +@APICommand(name = "listIpv4SubnetsForZone", + description = "Lists IPv4 subnets for zone.", + responseObject = DataCenterIpv4SubnetResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class ListIpv4SubnetsForZoneCmd extends BaseListCmd { + + @Parameter(name = ApiConstants.ID, + type = CommandType.UUID, + entityType = DataCenterIpv4SubnetResponse.class, + description = "UUID of the IPv4 subnet.") + private Long id; + + @Parameter(name = ApiConstants.ZONE_ID, + type = CommandType.UUID, + entityType = ZoneResponse.class, + description = "UUID of zone to which the IPv4 subnet belongs to.") + private Long zoneId; + + @Parameter(name = ApiConstants.SUBNET, + type = CommandType.STRING, + description = "CIDR of the IPv4 subnet.") + private String subnet; + + @Parameter(name = ApiConstants.ACCOUNT, + type = CommandType.STRING, + description = "the account which the IPv4 subnet is dedicated to. Must be used with the domainId parameter.") + private String accountName; + + @Parameter(name = ApiConstants.PROJECT_ID, + type = CommandType.UUID, + entityType = ProjectResponse.class, + description = "project who which the IPv4 subnet is dedicated to") + private Long projectId; + + @Parameter(name = ApiConstants.DOMAIN_ID, + type = CommandType.UUID, + entityType = DomainResponse.class, + description = "the domain ID which the IPv4 subnet is dedicated to.") + private Long domainId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public Long getZoneId() { + return zoneId; + } + + public String getSubnet() { + return subnet; + } + + public String getAccountName() { + return accountName; + } + + public Long getProjectId() { + return projectId; + } + + public Long getDomainId() { + return domainId; + } + + @Override + public void execute() { + List subnets = routedIpv4Manager.listDataCenterIpv4GuestSubnets(this); + ListResponse response = new ListResponse<>(); + List subnetResponses = new ArrayList<>(); + for (DataCenterIpv4GuestSubnet subnet : subnets) { + DataCenterIpv4SubnetResponse subnetResponse = routedIpv4Manager.createDataCenterIpv4SubnetResponse(subnet); + subnetResponses.add(subnetResponse); + } + + response.setResponses(subnetResponses, subnets.size()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } + +} 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/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 new file mode 100644 index 000000000000..a5e763c0cb00 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ReleaseDedicatedIpv4SubnetForZoneCmd.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.admin.network; + +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.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "releaseIpv4SubnetForZone", + description = "Releases an existing dedicated IPv4 subnet for a zone.", + responseObject = DataCenterIpv4SubnetResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class ReleaseDedicatedIpv4SubnetForZoneCmd extends BaseAsyncCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DataCenterIpv4SubnetResponse.class, required = true, description = "Id of the guest network IPv4 subnet") + private Long id; + + public Long getId() { + return id; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_ZONE_IP4_SUBNET_RELEASE; + } + + @Override + public String getEventDescription() { + return "Releasing dedicated zone IPv4 subnet with ID: " + getResourceUuid(ApiConstants.ID); + } + + @Override + public void execute() { + try { + DataCenterIpv4GuestSubnet result = routedIpv4Manager.releaseDedicatedDataCenterIpv4GuestSubnet(this); + if (result != null) { + DataCenterIpv4SubnetResponse response = routedIpv4Manager.createDataCenterIpv4SubnetResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to release guest network IPv4 subnet:" + getId()); + } + } catch (InvalidParameterValueException ex) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage()); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} 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 new file mode 100644 index 000000000000..db5daa505bee --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/UpdateIpv4SubnetForZoneCmd.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.admin.network; + +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.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "updateIpv4SubnetForZone", + description = "Updates an existing IPv4 subnet for a zone.", + responseObject = DataCenterIpv4SubnetResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class UpdateIpv4SubnetForZoneCmd extends BaseAsyncCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DataCenterIpv4SubnetResponse.class, required = true, description = "Id of the guest network IPv4 subnet") + private Long id; + + @Parameter(name = ApiConstants.SUBNET, + type = CommandType.STRING, + required = true, + description = "The new CIDR of the IPv4 subnet.") + private String subnet; + + public Long getId() { + return id; + } + + public String getSubnet() { + return subnet; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_ZONE_IP4_SUBNET_UPDATE; + } + + @Override + public String getEventDescription() { + return "Updating zone IPv4 subnet with ID: " + getResourceUuid(ApiConstants.ID); + } + + @Override + public void execute() { + try { + DataCenterIpv4GuestSubnet result = routedIpv4Manager.updateDataCenterIpv4GuestSubnet(this); + if (result != null) { + DataCenterIpv4SubnetResponse response = routedIpv4Manager.createDataCenterIpv4SubnetResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update guest network IPv4 subnet:" + getId()); + } + } catch (InvalidParameterValueException ex) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage()); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} 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 new file mode 100644 index 000000000000..3c58cbb3c532 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForNetworkCmd.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.network.bgp; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiArgValidator; +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.BgpPeerResponse; +import org.apache.cloudstack.api.response.NetworkResponse; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.Network; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +import java.util.List; + +@APICommand(name = "changeBgpPeersForNetwork", + description = "Change the BGP peers for a network.", + responseObject = BgpPeerResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class ChangeBgpPeersForNetworkCmd extends BaseAsyncCmd implements AdminCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NETWORK_ID, + type = CommandType.UUID, + entityType = NetworkResponse.class, + required = true, + description = "UUID of the network which the Bgp Peers are associated to.", + validations = {ApiArgValidator.PositiveNumber}) + private Long networkId; + + @Parameter(name = ApiConstants.BGP_PEER_IDS, + type = CommandType.LIST, + collectionType = CommandType.UUID, + entityType = BgpPeerResponse.class, + description = "Ids of the Bgp Peer. If it is empty, all BGP peers will be unlinked.") + private List bgpPeerIds; + + public Long getNetworkId() { + return networkId; + } + + public List getBgpPeerIds() { + return bgpPeerIds; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_NETWORK_BGP_PEER_UPDATE; + } + + @Override + public String getEventDescription() { + return "Changing BGP Peers for Network with ID: " + getResourceUuid(ApiConstants.NETWORK_ID); + } + + @Override + public void execute() { + try { + Network result = routedIpv4Manager.changeBgpPeersForNetwork(this); + if (result != null) { + NetworkResponse response = _responseGenerator.createNetworkResponse(getResponseView(), result); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to change BGP Peers for network"); + } + } catch (InvalidParameterValueException ex) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage()); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} 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 new file mode 100644 index 000000000000..8784f0672790 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForVpcCmd.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.network.bgp; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiArgValidator; +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.BgpPeerResponse; +import org.apache.cloudstack.api.response.VpcResponse; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.vpc.Vpc; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +import java.util.List; + +@APICommand(name = "changeBgpPeersForVpc", + description = "Change the BGP peers for a VPC.", + responseObject = BgpPeerResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class ChangeBgpPeersForVpcCmd extends BaseAsyncCmd implements AdminCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.VPC_ID, + type = CommandType.UUID, + entityType = VpcResponse.class, + required = true, + description = "UUID of the VPC which the Bgp Peers are associated to.", + validations = {ApiArgValidator.PositiveNumber}) + private Long vpcId; + + @Parameter(name = ApiConstants.BGP_PEER_IDS, + type = CommandType.LIST, + collectionType = CommandType.UUID, + entityType = BgpPeerResponse.class, + description = "Ids of the Bgp Peer. If it is empty, all BGP peers will be unlinked.") + private List bgpPeerIds; + + public Long getVpcId() { + return vpcId; + } + + public List getBgpPeerIds() { + return bgpPeerIds; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_VPC_BGP_PEER_UPDATE; + } + + @Override + public String getEventDescription() { + return "Changing BGP Peers for VPC with ID: " + getResourceUuid(ApiConstants.VPC_ID); + } + + @Override + public void execute() { + try { + Vpc result = routedIpv4Manager.changeBgpPeersForVpc(this); + if (result != null) { + VpcResponse response = _responseGenerator.createVpcResponse(getResponseView(), result); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to change BGP Peers for vpc"); + } + } catch (InvalidParameterValueException ex) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage()); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} 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 new file mode 100644 index 000000000000..f1d9b6723091 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/CreateBgpPeerCmd.java @@ -0,0 +1,168 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.bgp; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiArgValidator; +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.BgpPeerResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.network.BgpPeer; +import org.apache.commons.collections.MapUtils; + +import com.cloud.event.EventTypes; +import com.cloud.user.Account; + +import java.util.Collection; +import java.util.Map; + +@APICommand(name = "createBgpPeer", + description = "Creates a Bgp Peer for a zone.", + responseObject = BgpPeerResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = true, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class CreateBgpPeerCmd extends BaseAsyncCmd { + + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name = ApiConstants.ZONE_ID, + type = CommandType.UUID, + entityType = ZoneResponse.class, + required = true, + description = "UUID of the zone which the Bgp Peer belongs to.", + validations = {ApiArgValidator.PositiveNumber}) + private Long zoneId; + + @Parameter(name = ApiConstants.IP_ADDRESS, + type = CommandType.STRING, + description = "The IPv4 address of the Bgp Peer.") + private String ip4Address; + + @Parameter(name = ApiConstants.IP6_ADDRESS, + type = CommandType.STRING, + description = "The IPv6 address of the Bgp Peer.") + private String ip6Address; + + @Parameter(name = ApiConstants.AS_NUMBER, + type = CommandType.LONG, + required = true, + description = "The AS number of the Bgp Peer.") + private Long asNumber; + + @Parameter(name = ApiConstants.PASSWORD, + type = CommandType.STRING, + description = "The password of the Bgp Peer.") + private String password; + + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "account who will own the Bgp Peer") + private String accountName; + + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "project who will own the Bgp Peer") + private Long projectId; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "domain ID of the account owning the Bgp Peer") + private Long domainId; + + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, + description = "BGP peer details in key/value pairs.") + protected Map details; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public Long getZoneId() { + return zoneId; + } + + public String getIp4Address() { + return ip4Address; + } + + public String getIp6Address() { + return ip6Address; + } + + public String getPassword() { + return password; + } + + public Long getAsNumber() { + return asNumber; + } + + public String getAccountName() { + return accountName; + } + + public Long getProjectId() { + return projectId; + } + + public Long getDomainId() { + return domainId; + } + + public Map getDetails() { + if (MapUtils.isEmpty(details)) { + return null; + } + Collection paramsCollection = this.details.values(); + return (Map) (paramsCollection.toArray())[0]; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_BGP_PEER_CREATE; + } + + @Override + public String getEventDescription() { + return "Creating BGP Peer " + getAsNumber() + " for zone with ID: " + getResourceUuid(ApiConstants.ZONE_ID); + } + + @Override + public void execute() { + BgpPeer result = routedIpv4Manager.createBgpPeer(this); + if (result != null) { + BgpPeerResponse response = routedIpv4Manager.createBgpPeerResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Bgp Peer."); + } + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + +} 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 new file mode 100644 index 000000000000..f1ef963e9872 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/DedicateBgpPeerCmd.java @@ -0,0 +1,111 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.bgp; + +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.response.BgpPeerResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.network.BgpPeer; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "dedicateBgpPeer", + description = "Dedicates an existing Bgp Peer to an account or a domain.", + responseObject = BgpPeerResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class DedicateBgpPeerCmd extends BaseAsyncCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = BgpPeerResponse.class, required = true, description = "Id of the Bgp Peer") + private Long id; + + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "account who will own the Bgp Peer") + private String accountName; + + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "project who will own the Bgp Peer") + private Long projectId; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "domain ID of the account owning the Bgp Peer") + private Long domainId; + + public Long getId() { + return id; + } + + public String getAccountName() { + return accountName; + } + + public Long getProjectId() { + return projectId; + } + + public Long getDomainId() { + return domainId; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_BGP_PEER_DEDICATE; + } + + @Override + public String getEventDescription() { + return "Dedicating BGP Peer with ID: " + getResourceUuid(ApiConstants.ID); + } + + @Override + public void execute() { + try { + BgpPeer result = routedIpv4Manager.dedicateBgpPeer(this); + if (result != null) { + BgpPeerResponse response = routedIpv4Manager.createBgpPeerResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to dedicate Bgp Peer:" + getId()); + } + } catch (InvalidParameterValueException ex) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage()); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} 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 new file mode 100644 index 000000000000..a412e91bc48e --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/DeleteBgpPeerCmd.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.admin.network.bgp; + +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.response.BgpPeerResponse; +import org.apache.cloudstack.api.response.SuccessResponse; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "deleteBgpPeer", + description = "Deletes an existing Bgp Peer.", + responseObject = SuccessResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class DeleteBgpPeerCmd extends BaseAsyncCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = BgpPeerResponse.class, required = true, description = "Id of the Bgp Peer") + private Long id; + + public Long getId() { + return id; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_BGP_PEER_DELETE; + } + + @Override + public String getEventDescription() { + return "Deleting BGP Peer with ID: " + getResourceUuid(ApiConstants.ID); + } + + @Override + public void execute() { + try { + boolean result = routedIpv4Manager.deleteBgpPeer(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete Bgp Peer:" + getId()); + } + } catch (InvalidParameterValueException ex) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage()); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ListBgpPeersCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ListBgpPeersCmd.java new file mode 100644 index 000000000000..ea15f0970e87 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ListBgpPeersCmd.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.api.command.admin.network.bgp; + +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.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.BgpPeerResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.network.BgpPeer; + +@APICommand(name = "listBgpPeers", + description = "Lists Bgp Peers.", + responseObject = BgpPeerResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class ListBgpPeersCmd extends BaseListCmd { + + @Parameter(name = ApiConstants.ID, + type = CommandType.UUID, + entityType = BgpPeerResponse.class, + description = "UUID of the Bgp Peer.") + private Long id; + + @Parameter(name = ApiConstants.ZONE_ID, + type = CommandType.UUID, + entityType = ZoneResponse.class, + description = "UUID of zone to which the Bgp Peer belongs to.") + private Long zoneId; + + @Parameter(name = ApiConstants.AS_NUMBER, + type = CommandType.LONG, + description = "AS number of the Bgp Peer.") + private Long asNumber; + + @Parameter(name = ApiConstants.ACCOUNT, + type = CommandType.STRING, + description = "the account which the Bgp Peer is dedicated to. Must be used with the domainId parameter.") + private String accountName; + + @Parameter(name = ApiConstants.PROJECT_ID, + type = CommandType.UUID, + entityType = ProjectResponse.class, + description = "project who which the Bgp Peer is dedicated to") + private Long projectId; + + @Parameter(name = ApiConstants.DOMAIN_ID, + type = CommandType.UUID, + entityType = DomainResponse.class, + description = "the domain ID which the Bgp Peer is dedicated to.") + private Long domainId; + + @Parameter(name = ApiConstants.IS_DEDICATED, + type = CommandType.BOOLEAN, + description = "Lists only dedicated or non-dedicated Bgp Peers. If not set, lists all dedicated and non-dedicated BGP peers the domain/account can access.") + private Boolean isDedicated; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public Long getZoneId() { + return zoneId; + } + + public Long getAsNumber() { + return asNumber; + } + + public String getAccountName() { + return accountName; + } + + public Long getProjectId() { + return projectId; + } + + public Long getDomainId() { + return domainId; + } + + public Boolean getDedicated() { + return isDedicated; + } + + @Override + public void execute() { + List subnets = routedIpv4Manager.listBgpPeers(this); + ListResponse response = new ListResponse<>(); + List subnetResponses = new ArrayList<>(); + for (BgpPeer subnet : subnets) { + BgpPeerResponse subnetResponse = routedIpv4Manager.createBgpPeerResponse(subnet); + subnetResponse.setObjectName("bgppeer"); + subnetResponses.add(subnetResponse); + } + + response.setResponses(subnetResponses, subnets.size()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } + +} 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 new file mode 100644 index 000000000000..c754d443c051 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/ReleaseDedicatedBgpPeerCmd.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.admin.network.bgp; + +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.response.BgpPeerResponse; +import org.apache.cloudstack.network.BgpPeer; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "releaseBgpPeer", + description = "Releases an existing dedicated Bgp Peer.", + responseObject = BgpPeerResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class ReleaseDedicatedBgpPeerCmd extends BaseAsyncCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = BgpPeerResponse.class, required = true, description = "Id of the Bgp Peer") + private Long id; + + public Long getId() { + return id; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_BGP_PEER_RELEASE; + } + + @Override + public String getEventDescription() { + return "Releasing dedicated BGP Peer with ID: " + getResourceUuid(ApiConstants.ID); + } + + @Override + public void execute() { + try { + BgpPeer result = routedIpv4Manager.releaseDedicatedBgpPeer(this); + if (result != null) { + BgpPeerResponse response = routedIpv4Manager.createBgpPeerResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to release Bgp Peer:" + getId()); + } + } catch (InvalidParameterValueException ex) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage()); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} 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 new file mode 100644 index 000000000000..f45c1ee5a2f3 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/bgp/UpdateBgpPeerCmd.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.admin.network.bgp; + +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.response.BgpPeerResponse; +import org.apache.cloudstack.network.BgpPeer; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.commons.collections.MapUtils; + +import java.util.Collection; +import java.util.Map; + +@APICommand(name = "updateBgpPeer", + description = "Updates an existing Bgp Peer.", + responseObject = BgpPeerResponse.class, + since = "4.20.0", + requestHasSensitiveInfo = true, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class UpdateBgpPeerCmd extends BaseAsyncCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = BgpPeerResponse.class, required = true, description = "Id of the Bgp Peer") + private Long id; + + @Parameter(name = ApiConstants.IP_ADDRESS, + type = CommandType.STRING, + description = "The IPv4 address of the Bgp Peer.") + private String ip4Address; + + @Parameter(name = ApiConstants.IP6_ADDRESS, + type = CommandType.STRING, + description = "The IPv6 address of the Bgp Peer.") + private String ip6Address; + + @Parameter(name = ApiConstants.AS_NUMBER, + type = CommandType.LONG, + description = "The AS number of the Bgp Peer.") + private Long asNumber; + + @Parameter(name = ApiConstants.PASSWORD, + type = CommandType.STRING, + description = "The password of the Bgp Peer.") + private String password; + + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, + description = "BGP peer details in key/value pairs.") + protected Map details; + + @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 are removed for this resource; if false or not set, no action)") + private Boolean cleanupDetails; + + public Long getId() { + return id; + } + + public String getIp4Address() { + return ip4Address; + } + + public String getIp6Address() { + return ip6Address; + } + + public Long getAsNumber() { + return asNumber; + } + + public String getPassword() { + return password; + } + + public Map getDetails() { + if (MapUtils.isEmpty(details)) { + return null; + } + Collection paramsCollection = this.details.values(); + return (Map) (paramsCollection.toArray())[0]; + } + + public boolean isCleanupDetails(){ + return cleanupDetails == null ? false : cleanupDetails.booleanValue(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_BGP_PEER_UPDATE; + } + + @Override + public String getEventDescription() { + return "Updating BGP Peer with ID: " + getResourceUuid(ApiConstants.ID); + } + + @Override + public void execute() { + try { + BgpPeer result = routedIpv4Manager.updateBgpPeer(this); + if (result != null) { + BgpPeerResponse response = routedIpv4Manager.createBgpPeerResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update Bgp Peer:" + getId()); + } + } catch (InvalidParameterValueException ex) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage()); + } catch (CloudRuntimeException ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} 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 4562aa7da19e..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 @@ -29,14 +29,17 @@ import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DiskOfferingResponse; import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; +import org.apache.cloudstack.api.response.VgpuProfileResponse; import org.apache.cloudstack.api.response.VsphereStoragePoliciesResponse; import org.apache.cloudstack.api.response.ZoneResponse; -import org.apache.cloudstack.api.response.DiskOfferingResponse; +import org.apache.cloudstack.vm.lease.VMLeaseManager; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.EnumUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.collections.CollectionUtils; import com.cloud.exception.InvalidParameterValueException; import com.cloud.offering.ServiceOffering; @@ -51,139 +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 = "the CPU speed of the service offering in MHz.") + @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 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; @@ -222,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; @@ -242,6 +248,45 @@ public class CreateServiceOfferingCmd extends BaseCmd { @Parameter(name = ApiConstants.ENCRYPT_ROOT, type = CommandType.BOOLEAN, description = "VMs using this offering require root volume encryption", since="4.18") private Boolean encryptRoot; + @Parameter(name = ApiConstants.PURGE_RESOURCES, type = CommandType.BOOLEAN, + description = "Whether to cleanup instance and its associated resource from database upon expunge of the instance", + since="4.20") + private Boolean purgeResources; + + @Parameter(name = ApiConstants.INSTANCE_LEASE_DURATION, + type = CommandType.INTEGER, + description = "Number of days instance is leased for.", + since = "4.21.0") + private Integer leaseDuration; + + @Parameter(name = ApiConstants.INSTANCE_LEASE_EXPIRY_ACTION, type = CommandType.STRING, since = "4.21.0", + description = "Lease expiry action, valid values are STOP and DESTROY") + private String leaseExpiryAction; + + @Parameter(name = ApiConstants.VGPU_PROFILE_ID, + type = CommandType.UUID, + entityType = VgpuProfileResponse.class, + description = "the ID of the vGPU profile to which service offering should be mapped", + since = "4.21") + private Long vgpuProfileId; + + @Parameter(name = ApiConstants.GPU_COUNT, + type = CommandType.INTEGER, + description = "Count of GPUs to be used with this service offering. This is applicable only when passed with vGPU profile.", + since = "4.21") + private Integer gpuCount; + + @Parameter(name = ApiConstants.GPU_DISPLAY, + type = CommandType.BOOLEAN, + description = "Whether to enable GPU display for this service offering. This is applicable only when passed with vGPU profile. Defaults to false.", + since = "4.21") + private Boolean gpuDisplay; + + @Parameter(name = ApiConstants.EXTERNAL_DETAILS, + type = CommandType.MAP, + description = "Details in key/value pairs using format externaldetails[i].keyname=keyvalue. Example: externaldetails[0].endpoint.url=urlvalue", + since = "4.21.0") + private Map externalDetails; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -269,7 +314,7 @@ public Integer getMemory() { public String getServiceOfferingName() { if (StringUtils.isEmpty(serviceOfferingName)) { - throw new InvalidParameterValueException("Failed to create service offering because offering name has not been spified."); + throw new InvalidParameterValueException("Failed to create service offering because offering name has not been specified."); } return serviceOfferingName; } @@ -349,9 +394,15 @@ public Map getDetails() { } } } + + detailsMap.putAll(getExternalDetails()); return detailsMap; } + public Map getExternalDetails() { + return convertExternalDetailsToMap(externalDetails); + } + public Long getRootDiskSize() { return rootDiskSize; } @@ -477,6 +528,38 @@ public boolean getEncryptRoot() { return false; } + public VMLeaseManager.ExpiryAction getLeaseExpiryAction() { + if (StringUtils.isBlank(leaseExpiryAction)) { + return null; + } + VMLeaseManager.ExpiryAction action = EnumUtils.getEnumIgnoreCase(VMLeaseManager.ExpiryAction.class, leaseExpiryAction); + if (action == null) { + throw new InvalidParameterValueException("Invalid value configured for leaseexpiryaction, valid values are: " + + com.cloud.utils.EnumUtils.listValues(VMLeaseManager.ExpiryAction.values())); + } + return action; + } + + public Integer getLeaseDuration() { + return leaseDuration; + } + + public boolean isPurgeResources() { + return Boolean.TRUE.equals(purgeResources); + } + + public Long getVgpuProfileId() { + return vgpuProfileId; + } + + public Integer getGpuCount() { + return gpuCount; + } + + public Boolean getGpuDisplay() { + return Boolean.TRUE.equals(gpuDisplay); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/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 e94bff1fce84..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,10 +26,11 @@ 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") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "Account UUID", required = true) private Long id; @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/UpdateDiskOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/UpdateDiskOfferingCmd.java index 1bc0f2feb7ce..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,9 +16,9 @@ // 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; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -26,18 +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 ///////////////////// @@ -45,82 +45,86 @@ 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") + private String diskOfferingState; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -146,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() { @@ -260,6 +212,13 @@ public Long getIopsWriteRateMax() { public Long getIopsWriteRateMaxLength() { return iopsWriteRateMaxLength; } + public State getState() { + State state = EnumUtils.getEnumIgnoreCase(State.class, diskOfferingState); + if (StringUtils.isNotBlank(diskOfferingState) && state == null) { + throw new InvalidParameterValueException("Invalid state value: " + diskOfferingState); + } + return state; + } ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// 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 5a96212af04b..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,9 +16,10 @@ // under the License. package org.apache.cloudstack.api.command.admin.offering; -import java.util.ArrayList; import java.util.List; +import java.util.Map; +import com.cloud.offering.ServiceOffering.State; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -26,18 +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 ///////////////////// @@ -46,42 +47,67 @@ 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; + @Parameter(name = ApiConstants.STATE, + type = CommandType.STRING, + description = "state of the service offering") + private String serviceOfferingState; + + @Parameter(name = ApiConstants.PURGE_RESOURCES, type = CommandType.BOOLEAN, + description = "Whether to cleanup VM and its associated resource upon expunge", + since="4.20") + private Boolean purgeResources; + + @Parameter(name = ApiConstants.EXTERNAL_DETAILS, + type = CommandType.MAP, + description = "Details in key/value pairs using format externaldetails[i].keyname=keyvalue. Example: externaldetails[0].endpoint.url=urlvalue", + since = "4.21.0") + private Map externalDetails; + + @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 /////////////////////// ///////////////////////////////////////////////////// @@ -103,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() { @@ -170,6 +144,26 @@ public String getHostTags() { return hostTags; } + public State getState() { + State state = EnumUtils.getEnumIgnoreCase(State.class, serviceOfferingState); + if (StringUtils.isNotBlank(serviceOfferingState) && state == null) { + throw new InvalidParameterValueException("Invalid state value: " + serviceOfferingState); + } + return state; + } + + public boolean isPurgeResources() { + return Boolean.TRUE.equals(purgeResources); + } + + 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 dad6506729a3..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 @@ -26,6 +26,7 @@ import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiArgValidator; +import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseAsyncCmd; @@ -52,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; ///////////////////////////////////////////////////// @@ -69,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()); @@ -100,6 +101,16 @@ 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 + public Long getApiResourceId() { + return getHostId(); + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.Host; } } 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 699e28551852..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 @@ -26,6 +26,7 @@ import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiArgValidator; +import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseCmd; @@ -53,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; ///////////////////////////////////////////////////// @@ -112,4 +113,14 @@ protected void putOptionIfIsNotEmpty(ImmutableMap.Builder storageAccessGroups; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -95,6 +103,10 @@ public String getAllocationState() { return allocationState; } + public List getStorageAccessGroups() { + return storageAccessGroups; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -111,7 +123,7 @@ public ApiCommandResourceType getApiResourceType() { @Override public void execute() { - Pod result = _configService.createPod(getZoneId(), getPodName(), getStartIp(), getEndIp(), getGateway(), getNetmask(), getAllocationState()); + Pod result = _configService.createPod(getZoneId(), getPodName(), getStartIp(), getEndIp(), getGateway(), getNetmask(), getAllocationState(), getStorageAccessGroups()); if (result != null) { PodResponse response = _responseGenerator.createPodResponse(result, false); response.setResponseName(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/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 5ad0b457ced7..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,21 +40,26 @@ 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, + description = "the name of the storage access group", + since = "4.21.0") + private String storageAccessGroup; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -79,6 +84,18 @@ public Boolean getShowCapacities() { return showCapacities; } + public String getStorageAccessGroup() { + return storageAccessGroup; + } + + public ListPodsByCmd() { + + } + + public ListPodsByCmd(String storageAccessGroup) { + this.storageAccessGroup = storageAccessGroup; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -86,8 +103,8 @@ public Boolean getShowCapacities() { @Override public void execute() { Pair, Integer> result = _mgr.searchForPods(this); - ListResponse response = new ListResponse(); - List podResponses = new ArrayList(); + ListResponse response = new ListResponse<>(); + List podResponses = new ArrayList<>(); for (Pod pod : result.first()) { PodResponse podResponse = _responseGenerator.createPodResponse(pod, showCapacities); podResponse.setObjectName("pod"); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/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 new file mode 100644 index 000000000000..dcd4f2c89ef2 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/ListAlertTypesCmd.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.admin.resource; + +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; +import org.apache.cloudstack.api.response.AlertResponse; +import org.apache.cloudstack.api.response.AlertTypeResponse; +import org.apache.cloudstack.api.response.ListResponse; + +import com.cloud.user.Account; + +@APICommand(name = "listAlertTypes", description = "Lists all alerts types", responseObject = AlertResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class ListAlertTypesCmd extends BaseCmd { + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute() { + Set result = AlertService.AlertType.getAlertTypes(); + ListResponse response = new ListResponse<>(); + List typeResponseList = new ArrayList<>(); + for (AlertService.AlertType alertType : result) { + AlertTypeResponse alertResponse = new AlertTypeResponse(alertType.getType(), alertType.getName(), + alertType.isRepetitionAllowed()); + alertResponse.setObjectName("alerttype"); + typeResponseList.add(alertResponse); + } + response.setResponses(typeResponseList, result.size()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } +} 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 17a648f3a395..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.") @@ -71,6 +71,9 @@ public class ListCapacityCmd extends BaseListCmd { @Parameter(name = ApiConstants.SORT_BY, type = CommandType.STRING, since = "3.0.0", description = "Sort the results. Available values: Usage") private String sortBy; + @Parameter(name = ApiConstants.TAG, type = CommandType.STRING, description = "Tag for the resource type", since = "4.20.0") + private String tag; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -107,6 +110,10 @@ public String getSortBy() { return null; } + public String getTag() { + return tag; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -125,11 +132,12 @@ public void execute() { Collections.sort(capacityResponses, new Comparator() { public int compare(CapacityResponse resp1, CapacityResponse resp2) { int res = resp1.getZoneName().compareTo(resp2.getZoneName()); + // Group by zone if (res != 0) { return res; - } else { - return resp1.getCapacityType().compareTo(resp2.getCapacityType()); } + // Sort by capacity type only if not already sorted by usage + return (getSortBy() != null) ? 0 : resp1.getCapacityType().compareTo(resp2.getCapacityType()); } }); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/PurgeExpungedResourcesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/PurgeExpungedResourcesCmd.java new file mode 100644 index 000000000000..b6833f097336 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/PurgeExpungedResourcesCmd.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.resource; + + +import java.util.Date; + +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.ResponseObject; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.PurgeExpungedResourcesResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.resource.ResourceCleanupService; + +import com.cloud.event.EventTypes; + +@APICommand(name = "purgeExpungedResources", + description = "Purge expunged resources", + responseObject = SuccessResponse.class, + responseView = ResponseObject.ResponseView.Full, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}, + since = "4.20") +public class PurgeExpungedResourcesCmd extends BaseAsyncCmd { + + @Inject + ResourceCleanupService resourceCleanupService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.RESOURCE_TYPE, type = BaseCmd.CommandType.STRING, + description = "The type of the resource which need to be purged. Supported types: " + + "VirtualMachine") + private String resourceType; + + @Parameter(name = ApiConstants.BATCH_SIZE, type = CommandType.LONG, + description = "The size of batch used during purging") + private Long batchSize; + + @Parameter(name = ApiConstants.START_DATE, + type = CommandType.DATE, + description = "The start date range of the expunged resources used for purging " + + "(use format \"yyyy-MM-dd\" or \"yyyy-MM-dd HH:mm:ss\")") + private Date startDate; + + @Parameter(name = ApiConstants.END_DATE, + type = CommandType.DATE, + description = "The end date range of the expunged resources used for purging " + + "(use format \"yyyy-MM-dd\" or \"yyyy-MM-dd HH:mm:ss\")") + private Date endDate; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public String getResourceType() { + return resourceType; + } + + public Long getBatchSize() { + return batchSize; + } + + public Date getStartDate() { + return startDate; + } + + public Date getEndDate() { + return endDate; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_PURGE_EXPUNGED_RESOURCES; + } + + @Override + public String getEventDescription() { + return "Purging expunged resources"; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + try { + long result = resourceCleanupService.purgeExpungedResources(this); + PurgeExpungedResourcesResponse response = new PurgeExpungedResourcesResponse(); + response.setResourceCount(result); + response.setObjectName(getCommandName().toLowerCase()); + setResponseObject(response); + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getLocalizedMessage()); + } + } +} 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 e0cdc0dcf807..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 @@ -16,8 +16,6 @@ // under the License. package org.apache.cloudstack.api.command.admin.router; -import org.apache.commons.lang.BooleanUtils; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -32,7 +30,10 @@ import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.api.response.VpcResponse; import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.commons.lang.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import com.cloud.cpu.CPU; import com.cloud.network.router.VirtualRouter.Role; import com.cloud.vm.VirtualMachine; @@ -45,47 +46,52 @@ 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, + description = "CPU arch of the router", + since = "4.20.1") + private String arch; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -146,6 +152,10 @@ public boolean shouldFetchHealthCheckResults() { return BooleanUtils.isTrue(fetchHealthCheckResults); } + public CPU.CPUArch getArch() { + return StringUtils.isBlank(arch) ? null : CPU.CPUArch.fromType(arch); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/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/AddObjectStoragePoolCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/AddObjectStoragePoolCmd.java index b779ba2a2b47..460b8d642e90 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/AddObjectStoragePoolCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/AddObjectStoragePoolCmd.java @@ -56,6 +56,9 @@ public class AddObjectStoragePoolCmd extends BaseCmd { @Parameter(name = ApiConstants.TAGS, type = CommandType.STRING, description = "the tags for the storage pool") private String tags; + @Parameter(name = ApiConstants.SIZE, type = CommandType.LONG, description = "the total size of the object store in GiB. Used for tracking capacity and sending alerts", since = "4.21") + private Long size; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -68,6 +71,10 @@ public String getName() { return name; } + public Long getTotalSize() { + return size; + } + public Map getDetails() { Map detailsMap = null; if (details != null && !details.isEmpty()) { @@ -112,7 +119,7 @@ public long getEntityOwnerId() { @Override public void execute(){ try{ - ObjectStore result = _storageService.discoverObjectStore(getName(), getUrl(), getProviderName(), getDetails()); + ObjectStore result = _storageService.discoverObjectStore(getName(), getUrl(), getTotalSize(), getProviderName(), getDetails()); ObjectStoreResponse storeResponse = null; if (result != null) { storeResponse = _responseGenerator.createObjectStoreResponse(result); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/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 new file mode 100644 index 000000000000..3bb16dfea5a4 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ChangeStoragePoolScopeCmd.java @@ -0,0 +1,89 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.storage; + +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.ClusterResponse; +import org.apache.cloudstack.api.response.StoragePoolResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; + +import com.cloud.event.EventTypes; + +@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. " + + "There might be extra steps involved to make this work for other hypervisors and storage options.", + responseObject = SuccessResponse.class, since= "4.19.1", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class ChangeStoragePoolScopeCmd extends BaseAsyncCmd { + + @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.SCOPE, type = CommandType.STRING, required = true, description = "the scope of the storage: cluster or zone") + private String scope; + + @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, description = "the Id of the cluster to use if scope is being set to Cluster") + private Long clusterId; + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.StoragePool; + } + + @Override + public Long getApiResourceId() { + return getId(); + } + + public String getEventType() { + return EventTypes.EVENT_CHANGE_STORAGE_POOL_SCOPE; + } + + @Override + public String getEventDescription() { + return "Changing storage pool with ID: " + getResourceUuid(ApiConstants.ID) + " to scope " + scope; + } + + @Override + public void execute() { + _storageService.changeStoragePoolScope(this); + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } + + public Long getId() { + return id; + } + + public String getScope() { + return scope; + } + + public Long getClusterId() { + return clusterId; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ConfigureStorageAccessCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ConfigureStorageAccessCmd.java new file mode 100644 index 000000000000..c5459adfd2d0 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ConfigureStorageAccessCmd.java @@ -0,0 +1,135 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.storage; + +import java.util.List; + +import com.cloud.event.EventTypes; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.response.ClusterResponse; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.PodResponse; +import org.apache.cloudstack.api.response.StoragePoolResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.ZoneResponse; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; + +import com.cloud.user.Account; + +@APICommand(name = "configureStorageAccess", description = "Configure the storage access groups on zone/pod/cluster/host and storage, accordingly connections to the storage pools", responseObject = SuccessResponse.class, since = "4.21.0", + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class ConfigureStorageAccessCmd extends BaseAsyncCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "UUID of the zone") + private Long zoneId; + + @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "UUID of the pod") + private Long podId; + + @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, description = "UUID of the cluster") + private Long clusterId; + + @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "UUID of the host") + private Long hostId; + + @Parameter(name = ApiConstants.STORAGE_ID, type = CommandType.UUID, entityType = StoragePoolResponse.class, description = "UUID of the Storage Pool") + private Long storageId; + + @Parameter(name = ApiConstants.STORAGE_ACCESS_GROUPS, type = CommandType.LIST, collectionType = CommandType.STRING, + description = "comma separated list of storage access groups for connecting the storage pools and the hosts", + since = "4.21.0") + private List storageAccessGroups; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getZoneId() { + return zoneId; + } + + public Long getPodId() { + return podId; + } + + public Long getClusterId() { + return clusterId; + } + + public Long getHostId() { + return hostId; + } + + public Long getStorageId() { + return storageId; + } + + public List getStorageAccessGroups() { + return storageAccessGroups; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.StoragePool; + } + + @Override + public void execute() { + try { + boolean result = _storageService.configureStorageAccess(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to configure storage access"); + } + } catch (Exception e) { + logger.debug("Failed to configure storage access ", e); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to configure storage access, " + e.getMessage()); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_CONFIGURE_STORAGE_ACCESS; + } + + @Override + public String getEventDescription() { + return "Configuring storage access groups"; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/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 75813a7aabf5..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,46 +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.URL, type = CommandType.STRING, required = true, description = "the URL of the storage pool") + @Parameter(name = ApiConstants.STORAGE_ACCESS_GROUPS, type = CommandType.STRING, + description = "comma separated list of storage access groups for connecting to hosts having those specific groups", since = "4.21.0") + private String storageAccessGroups; + + @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = true, description = "The URL of the storage pool") private String url; - @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) @@ -115,6 +119,10 @@ public String getTags() { return tags; } + public String getStorageAccessGroups() { + return storageAccessGroups; + } + public String getUrl() { return url; } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/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 0848f4bd7ad2..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; ///////////////////////////////////////////////////// @@ -65,7 +65,7 @@ public ApiCommandResourceType getApiResourceType() { @Override public void execute() { - Pair, List> pools = _mgr.listStoragePoolsForMigrationOfVolume(getId()); + Pair, List> pools = _mgr.listStoragePoolsForMigrationOfVolume(getId(), getKeyword()); ListResponse response = new ListResponse(); List poolResponses = new ArrayList(); @@ -85,7 +85,8 @@ public void execute() { poolResponses.add(poolResponse); } sortPoolsBySuitabilityAndName(poolResponses); - response.setResponses(poolResponses); + List pagingList = com.cloud.utils.StringUtils.applyPagination(poolResponses, this.getStartIndex(), this.getPageSizeVal()); + response.setResponses(pagingList, poolResponses.size()); response.setResponseName(getCommandName()); this.setResponseObject(response); } 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/ListStorageAccessGroupsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStorageAccessGroupsCmd.java new file mode 100644 index 000000000000..d2a1757839f9 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStorageAccessGroupsCmd.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cloudstack.api.command.admin.storage; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.StorageAccessGroupResponse; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.response.ListResponse; + +@APICommand(name = "listStorageAccessGroups", description = "Lists storage access groups", responseObject = StorageAccessGroupResponse.class, since = "4.21.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class ListStorageAccessGroupsCmd extends BaseListCmd { + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NAME, type = BaseCmd.CommandType.STRING, description = "Name of the Storage access group") + private String name; + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + public String getName() { + return name; + } + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.StoragePool; + } + + @Override + public void execute() { + ListResponse response = _queryService.searchForStorageAccessGroups(this); + + response.setResponseName(getCommandName()); + + setResponseObject(response); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStoragePoolsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStoragePoolsCmd.java index 293ed3103cbc..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,37 +41,42 @@ public class ListStoragePoolsCmd extends BaseListCmd { @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, - description = "list storage pools belongig to the specific cluster") + description = "List storage pools belonging to the specific cluster") private Long clusterId; - @Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, description = "the IP address for the storage pool") + @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") private Long hostId; + @Parameter(name = ApiConstants.STORAGE_CUSTOM_STATS, type = CommandType.BOOLEAN, description = "If true, lists the custom stats of the storage pool", since = "4.18.1") + private Boolean customStats; + + @Parameter(name = ApiConstants.STORAGE_ACCESS_GROUP, type = CommandType.STRING, description = "the name of the storage access group", since = "4.21.0") + private String storageAccessGroup; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -129,6 +134,21 @@ public void setScope(String scope) { this.scope = scope; } + public Boolean getCustomStats() { + return customStats != null && customStats; + } + + public String getStorageAccessGroup() { + return storageAccessGroup; + } + + public ListStoragePoolsCmd() { + } + + public ListStoragePoolsCmd(String storageAccessGroup) { + this.storageAccessGroup = storageAccessGroup; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/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/UpdateImageStoreCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateImageStoreCmd.java index bcc438b957bf..0e1631a46ba2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateImageStoreCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateImageStoreCmd.java @@ -39,10 +39,17 @@ public class UpdateImageStoreCmd extends BaseCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ImageStoreResponse.class, required = true, description = "Image Store UUID") private Long id; - @Parameter(name = ApiConstants.READ_ONLY, type = CommandType.BOOLEAN, required = true, description = "If set to true, it designates the corresponding image store to read-only, " + - "hence not considering them during storage migration") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = false, description = "The new name for the Image Store.") + private String name; + + @Parameter(name = ApiConstants.READ_ONLY, type = CommandType.BOOLEAN, required = false, + description = "If set to true, it designates the corresponding image store to read-only, hence not considering them during storage migration") private Boolean readonly; + @Parameter(name = ApiConstants.CAPACITY_BYTES, type = CommandType.LONG, required = false, + description = "The number of bytes CloudStack can use on this image storage.\n\tNOTE: this will be overwritten by the StatsCollector as soon as there is a SSVM to query the storage.") + private Long capacityBytes; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -51,17 +58,25 @@ public Long getId() { return id; } + public String getName() { + return name; + } + public Boolean getReadonly() { return readonly; } + public Long getCapacityBytes() { + return capacityBytes; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @Override public void execute() { - ImageStore result = _storageService.updateImageStoreStatus(getId(), getReadonly()); + ImageStore result = _storageService.updateImageStore(this); ImageStoreResponse storeResponse = null; if (result != null) { storeResponse = _responseGenerator.createImageStoreResponse(result); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateObjectStoragePoolCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateObjectStoragePoolCmd.java index 497179d25ef1..ac007137ef1f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateObjectStoragePoolCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateObjectStoragePoolCmd.java @@ -44,6 +44,8 @@ public class UpdateObjectStoragePoolCmd extends BaseCmd { @Parameter(name = ApiConstants.URL, type = CommandType.STRING, description = "the url for the object store") private String url; + @Parameter(name = ApiConstants.SIZE, type = CommandType.LONG, description = "the total size of the object store in GiB. Used for tracking capacity and sending alerts. Set to 0 to stop tracking.", since = "4.21") + private Long size; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -61,6 +63,10 @@ public String getUrl() { return url; } + public Long getSize() { + return size; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// 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 13f02ef83c28..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 @@ -31,6 +31,8 @@ import com.cloud.storage.StoragePool; import com.cloud.user.Account; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.ObjectUtils; @SuppressWarnings("rawtypes") @APICommand(name = "updateStoragePool", description = "Updates a storage pool.", responseObject = StoragePoolResponse.class, since = "3.0.0", @@ -42,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; @@ -147,7 +149,19 @@ public void setUrl(String url) { @Override public void execute() { - StoragePool result = _storageService.updateStoragePool(this); + StoragePool result = null; + if (ObjectUtils.anyNotNull(name, capacityIops, capacityBytes, url, isTagARule, tags) || + MapUtils.isNotEmpty(details)) { + result = _storageService.updateStoragePool(this); + } else { + result = _storageService.getStoragePool(getId()); + } + + if (enabled != null) { + result = enabled ? _storageService.enablePrimaryStoragePool(id) + : _storageService.disablePrimaryStoragePool(id); + } + if (result != null) { StoragePoolResponse response = _responseGenerator.createStoragePoolResponse(result); response.setResponseName(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/heuristics/RemoveSecondaryStorageSelectorCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/heuristics/RemoveSecondaryStorageSelectorCmd.java index 79554f44782a..468c87d4d995 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/heuristics/RemoveSecondaryStorageSelectorCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/heuristics/RemoveSecondaryStorageSelectorCmd.java @@ -27,7 +27,7 @@ import org.apache.cloudstack.secstorage.heuristics.Heuristic; @APICommand(name = "removeSecondaryStorageSelector", description = "Removes an existing secondary storage selector.", since = "4.19.0", responseObject = - SecondaryStorageHeuristicsResponse.class, requestHasSensitiveInfo = false, entityType = {Heuristic.class}, responseHasSensitiveInfo = false, authorized = {RoleType.Admin}) + SuccessResponse.class, requestHasSensitiveInfo = false, entityType = {Heuristic.class}, responseHasSensitiveInfo = false, authorized = {RoleType.Admin}) public class RemoveSecondaryStorageSelectorCmd extends BaseCmd { @Parameter(name = ApiConstants.ID, type = BaseCmd.CommandType.UUID, entityType = SecondaryStorageHeuristicsResponse.class, required = true, description = "The unique identifier of the secondary storage selector to be removed.") 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 e8e5ee0ebad6..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 @@ -19,7 +19,6 @@ import java.util.ArrayList; import java.util.List; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -31,11 +30,13 @@ import org.apache.cloudstack.api.response.StoragePoolResponse; import org.apache.cloudstack.api.response.SystemVmResponse; import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.commons.lang3.StringUtils; +import com.cloud.cpu.CPU; import com.cloud.utils.Pair; import com.cloud.vm.VirtualMachine; -@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 { @@ -44,36 +45,41 @@ 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; + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + description = "CPU arch of the system VM", + since = "4.20.1") + private String arch; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -110,6 +116,10 @@ public Long getStorageId() { return storageId; } + public CPU.CPUArch getArch() { + return StringUtils.isBlank(arch) ? null : CPU.CPUArch.fromType(arch); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/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 4f4b26316673..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,13 +39,13 @@ 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, description = "If true, initiates copy of scripts and restart of the agent, even if the scripts version matches." + "To be used with ID parameter only") - private Boolean force; + private Boolean forced; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -58,7 +57,7 @@ public Long getId() { } public boolean isForced() { - return force != null && force; + return forced != null && forced; } ///////////////////////////////////////////////////// @@ -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 06e57674c537..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; ///////////////////////////////////////////////////// @@ -74,7 +74,7 @@ public Long getServiceOfferingId() { } public Map getDetails() { - return details; + return convertDetailsToMap(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 5abe90e3f589..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; ///////////////////////////////////////////////////// @@ -68,7 +68,7 @@ public Long getServiceOfferingId() { } public Map getDetails() { - return details; + return convertDetailsToMap(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/GenerateUsageRecordsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/GenerateUsageRecordsCmd.java index 491b0fe85bae..a0314586d92d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/GenerateUsageRecordsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/GenerateUsageRecordsCmd.java @@ -47,13 +47,13 @@ public class GenerateUsageRecordsCmd extends BaseCmd { @Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, - required = true, + required = false, description = "End date range for usage record query. Use yyyy-MM-dd as the date format, e.g. startDate=2009-06-03.") private Date endDate; @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, - required = true, + required = false, description = "Start date range for usage record query. Use yyyy-MM-dd as the date format, e.g. startDate=2009-06-01.") private Date startDate; 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/ListUsageRecordsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/ListUsageRecordsCmd.java index 3cb148c2af03..9ce1fcb2bc99 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/ListUsageRecordsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/ListUsageRecordsCmd.java @@ -53,16 +53,12 @@ public class ListUsageRecordsCmd extends BaseListCmd { @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "List usage records for the specified domain.") private Long domainId; - @Parameter(name = ApiConstants.END_DATE, - type = CommandType.DATE, - required = true, - description = "End date range for usage record query (use format \"yyyy-MM-dd\" or the new format \"yyyy-MM-dd HH:mm:ss\", e.g. startDate=2015-01-01 or startdate=2015-01-01 10:30:00).") + @Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, required = true, description = "End date range for usage record query. " + + ApiConstants.PARAMETER_DESCRIPTION_END_DATE_POSSIBLE_FORMATS) private Date endDate; - @Parameter(name = ApiConstants.START_DATE, - type = CommandType.DATE, - required = true, - description = "Start date range for usage record query (use format \"yyyy-MM-dd\" or the new format \"yyyy-MM-dd HH:mm:ss\", e.g. startDate=2015-01-01 or startdate=2015-01-01 11:00:00).") + @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, required = true, description = "Start date range for usage record query. " + + ApiConstants.PARAMETER_DESCRIPTION_START_DATE_POSSIBLE_FORMATS) private Date startDate; @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "List usage records for the specified account") @@ -137,11 +133,11 @@ public void setDomainId(Long domainId) { } public void setEndDate(Date endDate) { - this.endDate = endDate == null ? null : new Date(endDate.getTime()); + this.endDate = endDate; } public void setStartDate(Date startDate) { - this.startDate = startDate == null ? null : new Date(startDate.getTime()); + this.startDate = startDate; } public void setAccountId(Long accountId) { @@ -167,8 +163,8 @@ public Boolean isRecursive() { @Override public void execute() { Pair, Integer> usageRecords = _usageService.getUsageRecords(this); - ListResponse response = new ListResponse(); - List usageResponses = new ArrayList(); + ListResponse response = new ListResponse<>(); + List usageResponses = new ArrayList<>(); Map> resourceTagResponseMap = null; if (usageRecords != null) { //read the resource tags details for all the resources in usage data and store in Map diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/ListUsageTypesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/ListUsageTypesCmd.java index 2772743c75a4..b993735dba76 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/ListUsageTypesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/ListUsageTypesCmd.java @@ -23,6 +23,7 @@ import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.UsageTypeResponse; +import org.apache.cloudstack.usage.UsageTypes; import com.cloud.user.Account; @@ -37,8 +38,8 @@ public long getEntityOwnerId() { @Override public void execute() { - List result = _usageService.listUsageTypes(); - ListResponse response = new ListResponse(); + List result = UsageTypes.listUsageTypes(); + ListResponse response = new ListResponse<>(); response.setResponses(result); response.setResponseName(getCommandName()); this.setResponseObject(response); 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 560e449412c2..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,13 +82,12 @@ 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) { - SuccessResponse response = new SuccessResponse(getCommandName()); - this.setResponseObject(response); - } else { + if (!result) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete user"); } + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); } } 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/GetUserKeysCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/GetUserKeysCmd.java index 3a3414d95d8a..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 @@ -20,45 +20,48 @@ import com.cloud.user.Account; import com.cloud.user.User; +import com.cloud.utils.Pair; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.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(){ - Map keys = _accountService.getKeys(this); - RegisterResponse response = new RegisterResponse(); - if(keys != null){ - response.setApiKey(keys.get("apikey")); - response.setSecretKey(keys.get("secretkey")); + + public void execute() { + Pair> keys = _accountService.getKeys(this); + + RegisterUserKeyResponse response = new RegisterUserKeyResponse(); + if (keys != null){ + response.setApiKeyAccess(keys.first()); + response.setApiKey(keys.second().get("apikey")); + response.setSecretKey(keys.second().get("secretkey")); } response.setObjectName("userkeys"); 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 ef9e3fa22405..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 @@ -16,23 +16,29 @@ // under the License. package org.apache.cloudstack.api.command.admin.user; +import com.cloud.exception.InvalidParameterValueException; import com.cloud.server.ResourceIcon; import com.cloud.server.ResourceTag; import com.cloud.user.Account; +import com.cloud.user.User; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.command.user.UserCmd; import org.apache.cloudstack.api.response.ResourceIconResponse; 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.ResponseObject.ResponseView; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.UserResponse; +import org.apache.commons.lang3.EnumUtils; import java.util.List; @APICommand(name = "listUsers", description = "Lists user accounts", responseObject = UserResponse.class, - requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) -public class ListUsersCmd extends BaseListAccountResourcesCmd { + responseView = ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) +public class ListUsersCmd extends BaseListAccountResourcesCmd implements UserCmd { ///////////////////////////////////////////////////// @@ -53,10 +59,17 @@ public class ListUsersCmd extends BaseListAccountResourcesCmd { @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, description = "List user by the username") private String username; + @Parameter(name = ApiConstants.API_KEY_ACCESS, type = CommandType.STRING, description = "List users 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 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", + description = "List users by their authentication source. Valid values are: native, ldap, saml2 and saml2disabled.") + private String userSource; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -77,17 +90,38 @@ public String getUsername() { return username; } + public String getApiKeyAccess() { + return apiKeyAccess; + } + public Boolean getShowIcon() { return showIcon != null ? showIcon : false; } + public User.Source getUserSource() { + if (userSource == null) { + return null; + } + + User.Source source = EnumUtils.getEnumIgnoreCase(User.Source.class, userSource); + if (source == null || List.of(User.Source.OAUTH2, User.Source.UNKNOWN).contains(source)) { + throw new InvalidParameterValueException(String.format("Invalid user source: %s. Valid values are: native, ldap, saml2 and saml2disabled.", userSource)); + } + + if (source == User.Source.NATIVE) { + return User.Source.UNKNOWN; + } + + return source; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @Override public void execute() { - ListResponse response = _queryService.searchForUsers(this); + ListResponse response = _queryService.searchForUsers(getResponseView(), this); response.setResponseName(getCommandName()); this.setResponseObject(response); if (response != null && response.getCount() > 0 && getShowIcon()) { 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..11d7c1d2ffab --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/RegisterUserKeysCmd.java @@ -0,0 +1,209 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.") + 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)) { + 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 3f8d386d2669..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 @@ -18,6 +18,7 @@ import javax.inject.Inject; +import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -28,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; @@ -37,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, @@ -66,8 +70,11 @@ 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.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; @Parameter(name = ApiConstants.TIMEZONE, type = CommandType.STRING, @@ -81,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() { @@ -117,7 +127,11 @@ public String getCurrentPassword() { } public String getSecretKey() { - return secretKey; + return userSecretKey; + } + + public String getApiKeyAccess() { + return apiKeyAccess; } public String getTimezone() { @@ -148,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) { @@ -185,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 cceadea85322..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 @@ -16,7 +16,10 @@ // under the License. package org.apache.cloudstack.api.command.admin.vlan; +import com.cloud.configuration.ConfigurationService; +import com.cloud.network.Network; import com.cloud.utils.net.NetUtils; +import com.cloud.utils.StringUtils; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -39,6 +42,7 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.user.Account; + @APICommand(name = "createVlanIpRange", description = "Creates a VLAN IP range.", responseObject = VlanIpRangeResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateVlanIpRangeCmd extends BaseCmd { @@ -50,68 +54,71 @@ 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") + private String provider; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -152,8 +159,12 @@ public String getStartIp() { return startIp; } + public Network.Provider getProvider() { + return Network.Provider.getProvider(provider); + } + public String getVlan() { - if (vlan == null || vlan.isEmpty()) { + if (StringUtils.isBlank(vlan) && !ConfigurationService.IsIpRangeForProvider(getProvider())) { vlan = "untagged"; } return vlan; @@ -221,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 7f583fe225af..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 @@ -28,7 +28,7 @@ import com.cloud.user.Account; -@APICommand(name = "deleteVlanIpRange", description = "Creates a VLAN IP range.", responseObject = SuccessResponse.class, +@APICommand(name = "deleteVlanIpRange", description = "Deletes a VLAN IP range.", responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class DeleteVlanIpRangeCmd extends BaseCmd { @@ -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 ac63a5efac37..e11d20d06466 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,8 +81,8 @@ 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; ///////////////////////////////////////////////////// @@ -120,27 +120,16 @@ public List getSecurityGroupIdList() { @Override public void execute() { try { - UserVm userVm = _userVmService.moveVMToUser(this); - if (userVm == null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to move vm"); - } + UserVm userVm = _userVmService.moveVmToUser(this); UserVmResponse response = _responseGenerator.createUserVmResponse(ResponseView.Full, "virtualmachine", userVm).get(0); response.setResponseName(getCommandName()); setResponseObject(response); - } catch (InvalidParameterValueException e){ - e.printStackTrace(); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); } catch (Exception e) { - logger.error("Failed to move vm due to: " + e.getStackTrace()); - if (e.getMessage() != null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to move vm due to " + e.getMessage()); - } else if (e.getCause() != null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to move vm due to " + e.getCause()); - } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to move vm"); - } + ApiErrorCode errorCode = e instanceof InvalidParameterValueException ? ApiErrorCode.PARAM_ERROR : ApiErrorCode.INTERNAL_ERROR; + String msg = String.format("Failed to move Instance due to [%s].", getVmId()); + logger.error(msg, e); + throw new ServerApiException(errorCode, msg); } - } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/CreateVMFromBackupCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/CreateVMFromBackupCmdByAdmin.java new file mode 100644 index 000000000000..d95f17ef304c --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/CreateVMFromBackupCmdByAdmin.java @@ -0,0 +1,55 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.vm; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.command.admin.AdminCmd; +import org.apache.cloudstack.api.command.user.vm.CreateVMFromBackupCmd; +import org.apache.cloudstack.api.response.ClusterResponse; +import org.apache.cloudstack.api.response.PodResponse; +import org.apache.cloudstack.api.response.UserVmResponse; + +import com.cloud.vm.VirtualMachine; + +@APICommand(name = "createVMFromBackup", + description = "Creates and automatically starts a VM from a backup.", + responseObject = UserVmResponse.class, + responseView = ResponseObject.ResponseView.Full, + entityType = {VirtualMachine.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = true, + since = "4.21.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class CreateVMFromBackupCmdByAdmin extends CreateVMFromBackupCmd implements AdminCmd { + + @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "destination Pod ID to deploy the VM to - parameter available for root admin only", since = "4.21") + private Long podId; + + @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, description = "destination Cluster ID to deploy the VM to - parameter available for root admin only", since = "4.21") + private Long clusterId; + + public Long getPodId() { + return podId; + } + + public Long getClusterId() { + return clusterId; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/DeployVMCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/DeployVMCmdByAdmin.java index 6bb7657b86ba..e64c8b3f46c6 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,15 +30,15 @@ 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; public Long getPodId() { 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..93d4b610b900 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 @@ -24,7 +24,7 @@ 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 {} 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 dd897218a4d3..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.isTraceEnabled()) { - logger.trace(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.isTraceEnabled()) { - logger.trace(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 1a34b7ea6cce..f7940460d6cd 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 @@ -37,6 +37,7 @@ import org.apache.cloudstack.api.response.VmwareDatacenterResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.vm.VmImportService; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; @@ -116,40 +117,60 @@ public class ImportVmCmd extends ImportUnmanagedInstanceCmd { description = "Temp Path on external host for disk image copy" ) private String tmpPath; - // Import from Vmware to KVM migration parameters + // Import from VMware to KVM migration parameters @Parameter(name = ApiConstants.EXISTING_VCENTER_ID, type = CommandType.UUID, entityType = VmwareDatacenterResponse.class, - description = "(only for importing migrated VMs from Vmware to KVM) UUID of a linked existing vCenter") + description = "(only for importing VMs from VMware to KVM) UUID of a linked existing vCenter") private Long existingVcenterId; @Parameter(name = ApiConstants.HOST_IP, type = BaseCmd.CommandType.STRING, - description = "(only for importing migrated VMs from Vmware to KVM) VMware ESXi host IP/Name.") + description = "(only for importing VMs from VMware to KVM) VMware ESXi host IP/Name.") private String hostip; @Parameter(name = ApiConstants.VCENTER, type = CommandType.STRING, - description = "(only for importing migrated VMs from Vmware to KVM) The name/ip of vCenter. Make sure it is IP address or full qualified domain name for host running vCenter server.") + description = "(only for importing VMs from VMware to KVM) 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.DATACENTER_NAME, type = CommandType.STRING, - description = "(only for importing migrated VMs from Vmware to KVM) Name of VMware datacenter.") + description = "(only for importing VMs from VMware to KVM) Name of VMware datacenter.") private String datacenterName; @Parameter(name = ApiConstants.CLUSTER_NAME, type = CommandType.STRING, - description = "(only for importing migrated VMs from Vmware to KVM) Name of VMware cluster.") + description = "(only for importing VMs from VMware to KVM) Name of VMware cluster.") private String clusterName; @Parameter(name = ApiConstants.CONVERT_INSTANCE_HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, - description = "(only for importing migrated VMs from Vmware to KVM) optional - the host to perform the virt-v2v migration from VMware to KVM.") + description = "(only for importing VMs from VMware to KVM) optional - the host to perform the virt-v2v conversion from VMware to KVM.") private Long convertInstanceHostId; + @Parameter(name = ApiConstants.IMPORT_INSTANCE_HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, since = "4.19.2", + description = "(only for importing VMs from VMware to KVM) optional - the host to import the converted instance from VMware to KVM.") + private Long importInstanceHostId; + @Parameter(name = ApiConstants.CONVERT_INSTANCE_STORAGE_POOL_ID, type = CommandType.UUID, entityType = StoragePoolResponse.class, - description = "(only for importing migrated VMs from Vmware to KVM) optional - the temporary storage pool to perform the virt-v2v migration from VMware to KVM.") + description = "(only for importing VMs from VMware to KVM) optional - the temporary storage pool to perform the virt-v2v migration from VMware to KVM.") private Long convertStoragePoolId; + @Parameter(name = ApiConstants.FORCE_MS_TO_IMPORT_VM_FILES, type = CommandType.BOOLEAN, + 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; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -194,10 +215,18 @@ public Long getConvertInstanceHostId() { return convertInstanceHostId; } + public Long getImportInstanceHostId() { + return importInstanceHostId; + } + public Long getConvertStoragePoolId() { return convertStoragePoolId; } + public Boolean getForceMsToImportVmFiles() { + return BooleanUtils.toBooleanDefaultIfNull(forceMsToImportVmFiles, false); + } + public String getHypervisor() { return hypervisor; } @@ -231,6 +260,14 @@ public String getEventType() { return EventTypes.EVENT_VM_IMPORT; } + public String getExtraParams() { + return extraParams; + } + + public boolean getForceConvertToPool() { + return BooleanUtils.toBooleanDefaultIfNull(forceConvertToPool, false); + } + @Override public String getEventDescription() { String vmName = getName(); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ListAffectedVmsForStorageScopeChangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ListAffectedVmsForStorageScopeChangeCmd.java new file mode 100644 index 000000000000..d586a81b6856 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ListAffectedVmsForStorageScopeChangeCmd.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cloudstack.api.command.admin.vm; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.ClusterResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.StoragePoolResponse; +import org.apache.cloudstack.api.response.VirtualMachineResponse; + +import com.cloud.vm.VirtualMachine; + +@APICommand(name = "listAffectedVmsForStorageScopeChange", + description = "List user and system VMs that need to be stopped and destroyed respectively for changing the scope of the storage pool from Zone to Cluster.", + responseObject = VirtualMachineResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.19.1", + authorized = {RoleType.Admin}) +public class ListAffectedVmsForStorageScopeChangeCmd extends BaseListCmd { + + @Parameter(name = ApiConstants.CLUSTER_ID, + type = CommandType.UUID, + entityType = ClusterResponse.class, + required = true, + description = "the Id of the cluster the scope of the storage pool is being changed to") + private Long clusterIdForScopeChange; + + @Parameter(name = ApiConstants.STORAGE_ID, + type = CommandType.UUID, + entityType = StoragePoolResponse.class, + required = true, + description = "the Id of the storage pool on which change scope operation is being done") + private Long storageId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getClusterIdForScopeChange() { + return clusterIdForScopeChange; + } + + public Long getStorageId() { + return storageId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + ListResponse response = _queryService.listAffectedVmsForStorageScopeChange(this); + response.setResponseName(getCommandName()); + response.setObjectName(VirtualMachine.class.getSimpleName().toLowerCase()); + setResponseObject(response); + } +} 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/ListVnfAppliancesCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ListVnfAppliancesCmdByAdmin.java new file mode 100644 index 000000000000..1a820ab9a483 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ListVnfAppliancesCmdByAdmin.java @@ -0,0 +1,36 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.vm; + +import com.cloud.vm.VirtualMachine; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ResponseObject.ResponseView; +import org.apache.cloudstack.api.command.admin.AdminCmd; + +import org.apache.cloudstack.api.command.user.vm.ListVnfAppliancesCmd; +import org.apache.cloudstack.api.response.UserVmResponse; + +@APICommand(name = "listVnfAppliances", description = "List VNF appliance owned by the account.", + responseObject = UserVmResponse.class, + responseView = ResponseView.Full, + entityType = {VirtualMachine.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}, + since = "4.19.1") +public class ListVnfAppliancesCmdByAdmin extends ListVnfAppliancesCmd implements AdminCmd { +} 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 8881a2bc354e..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 @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.admin.vm; +import com.cloud.hypervisor.Hypervisor; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.APICommand; @@ -44,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) @@ -59,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, @@ -123,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 @@ -142,7 +143,11 @@ 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())) { + throw new InvalidParameterValueException("Migrate VM instance operation is not allowed for External hypervisor type"); } Host destinationHost = null; @@ -151,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"); } @@ -179,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/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 new file mode 100644 index 000000000000..50f4b9c1fbe5 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/ImportVolumeCmd.java @@ -0,0 +1,165 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.volume; + +import com.cloud.event.EventTypes; +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.BaseAsyncCmd; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DiskOfferingResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.api.response.StoragePoolResponse; +import org.apache.cloudstack.api.response.VolumeResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.storage.volume.VolumeImportUnmanageService; + +import javax.inject.Inject; + +@APICommand(name = "importVolume", + description = "Import an unmanaged volume from a storage pool on a host into CloudStack", + responseObject = VolumeResponse.class, + responseView = ResponseObject.ResponseView.Full, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = true, + authorized = {RoleType.Admin}, + since = "4.19.1") +public class ImportVolumeCmd extends BaseAsyncCmd { + + @Inject + public VolumeImportUnmanageService volumeImportService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + + @Parameter(name = ApiConstants.PATH, + type = BaseCmd.CommandType.STRING, + required = true, + description = "the path of the volume") + private String path; + + @Parameter(name = ApiConstants.NAME, + type = BaseCmd.CommandType.STRING, + description = "the name of the volume. If not set, it will be set to the path of the volume.") + private String name; + + @Parameter(name = ApiConstants.STORAGE_ID, + type = BaseCmd.CommandType.UUID, + required = true, + entityType = StoragePoolResponse.class, + description = "the ID of the storage pool") + private Long storageId; + + @Parameter(name = ApiConstants.DISK_OFFERING_ID, + type = BaseCmd.CommandType.UUID, + entityType = DiskOfferingResponse.class, + description = "the ID of the disk offering linked to the volume") + private Long diskOfferingId; + + @Parameter(name = ApiConstants.ACCOUNT, + type = BaseCmd.CommandType.STRING, + description = "an optional account for the volume. Must be used with domainId.") + private String accountName; + + @Parameter(name = ApiConstants.DOMAIN_ID, + type = BaseCmd.CommandType.UUID, + entityType = DomainResponse.class, + description = "import volume to the domain specified") + private Long domainId; + + @Parameter(name = ApiConstants.PROJECT_ID, + type = BaseCmd.CommandType.UUID, + entityType = ProjectResponse.class, + description = "import volume for the project") + private Long projectId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getPath() { + return path; + } + + public String getName() { + return name; + } + + public Long getStorageId() { + return storageId; + } + + public Long getDiskOfferingId() { + return diskOfferingId; + } + + public String getAccountName() { + return accountName; + } + + public Long getDomainId() { + return domainId; + } + + public Long getProjectId() { + return projectId; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_VOLUME_IMPORT; + } + + @Override + public String getEventDescription() { + return String.format("Importing unmanaged Volume with path: %s", path); + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + VolumeResponse response = volumeImportService.importVolume(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); + if (accountId == null) { + return CallContext.current().getCallingAccount().getId(); + } + return accountId; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/ListVolumesForImportCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/ListVolumesForImportCmd.java new file mode 100644 index 000000000000..dbe3d37e4069 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/ListVolumesForImportCmd.java @@ -0,0 +1,93 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.volume; + +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.BaseCmd; +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.ListResponse; +import org.apache.cloudstack.api.response.StoragePoolResponse; +import org.apache.cloudstack.api.response.VolumeForImportResponse; +import org.apache.cloudstack.storage.volume.VolumeImportUnmanageService; +import org.apache.cloudstack.storage.volume.VolumeOnStorageTO; + +import javax.inject.Inject; + +@APICommand(name = "listVolumesForImport", + description = "Lists unmanaged volumes on a storage pool", + responseObject = VolumeForImportResponse.class, + responseView = ResponseObject.ResponseView.Full, + entityType = {VolumeOnStorageTO.class}, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = true, + authorized = {RoleType.Admin}, + since = "4.19.1") +public class ListVolumesForImportCmd extends BaseListCmd { + + @Inject + public VolumeImportUnmanageService volumeImportService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.STORAGE_ID, + type = BaseCmd.CommandType.UUID, + required = true, + entityType = StoragePoolResponse.class, + description = "the ID of the storage pool") + private Long storageId; + + @Parameter(name = ApiConstants.PATH, + type = BaseCmd.CommandType.STRING, + description = "the path of the volume on the storage pool") + private String path; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getStorageId() { + return storageId; + } + + public String getPath() { + return path; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + ListResponse response = volumeImportService.listVolumesForImport(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } +} 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 new file mode 100644 index 000000000000..ac573dd4ecb9 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/UnmanageVolumeCmd.java @@ -0,0 +1,127 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.volume; + +import com.cloud.event.EventTypes; +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.storage.Volume; +import com.cloud.user.Account; + +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.SuccessResponse; +import org.apache.cloudstack.api.response.VolumeResponse; +import org.apache.cloudstack.storage.volume.VolumeImportUnmanageService; + +import javax.inject.Inject; + +@APICommand(name = "unmanageVolume", + description = "Unmanage a volume on storage pool.", + entityType = {Volume.class}, + responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, + authorized = {RoleType.Admin}, + since = "4.19.1") +public class UnmanageVolumeCmd extends BaseAsyncCmd { + + @Inject + public VolumeImportUnmanageService volumeImportService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, + type = CommandType.UUID, + entityType = VolumeResponse.class, + required = true, + description = "The ID of the volume to unmanage") + private Long volumeId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public Long getVolumeId() { + return volumeId; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_VOLUME_UNMANAGE; + } + + @Override + public String getEventDescription() { + return "Unmanaging Volume with ID: " + getResourceUuid(ApiConstants.ID); + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, + ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try { + boolean result = volumeImportService.unmanageVolume(volumeId); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to unmanage the volume"); + } + + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getLocalizedMessage()); + } + } + + @Override + public long getEntityOwnerId() { + Volume volume = _responseGenerator.findVolumeById(volumeId); + if (volume != null) { + return volume.getAccountId(); + } + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.Volume; + } + + @Override + public Long getApiResourceId() { + return volumeId; + } + +} 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/CreateVPCCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCCmdByAdmin.java index bd00876ed36c..9dc31f8cefec 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCCmdByAdmin.java @@ -17,13 +17,31 @@ package org.apache.cloudstack.api.command.admin.vpc; 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.vpc.CreateVPCCmd; +import org.apache.cloudstack.api.response.BgpPeerResponse; import org.apache.cloudstack.api.response.VpcResponse; import com.cloud.network.vpc.Vpc; +import java.util.List; + @APICommand(name = "createVPC", description = "Creates a VPC", responseObject = VpcResponse.class, responseView = ResponseView.Full, entityType = {Vpc.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) -public class CreateVPCCmdByAdmin extends CreateVPCCmd implements AdminCmd {} +public class CreateVPCCmdByAdmin extends CreateVPCCmd implements AdminCmd { + @Parameter(name = ApiConstants.BGP_PEER_IDS, + type = CommandType.LIST, + collectionType = CommandType.UUID, + entityType = BgpPeerResponse.class, + description = "Ids of the Bgp Peer for the VPC", + since = "4.20.0") + private List bgpPeerIds; + + + public List getBgpPeerIds() { + return bgpPeerIds; + } +} 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 382c081e01e2..6b425bc10d21 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.command.admin.vpc; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; @@ -24,10 +25,17 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.Network; +import com.cloud.network.VirtualRouterProvider; +import com.cloud.offering.NetworkOffering; import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.apache.cloudstack.api.APICommand; @@ -44,6 +52,20 @@ import com.cloud.network.vpc.VpcOffering; 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.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 { @@ -52,59 +74,93 @@ 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, - required = true, 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; + @Deprecated + @Parameter(name = ApiConstants.FOR_NSX, + type = CommandType.BOOLEAN, + description = "true if network offering is meant to be used for NSX, false otherwise.", + since = "4.20.0") + private Boolean forNsx; + + @Parameter(name = ApiConstants.PROVIDER, + type = CommandType.STRING, + description = "Name of the provider providing the service", + since = "4.21.0") + private String provider; + + @Parameter(name = ApiConstants.NSX_SUPPORT_LB, + type = CommandType.BOOLEAN, + description = "true if network offering for NSX VPC offering supports Load balancer service.", + since = "4.20.0") + private Boolean nsxSupportsLbService; + @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; + @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.SPECIFY_AS_NUMBER, type = CommandType.BOOLEAN, since = "4.20.0", + description = "true if the VPC 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 VPC offering. Supported types are: Static or Dynamic.") + private String routingMode; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -117,21 +173,58 @@ public String getDisplayText() { return StringUtils.isEmpty(displayText) ? vpcOfferingName : displayText; } + public boolean isExternalNetworkProvider() { + return Arrays.asList("NSX", "Netris").stream() + .anyMatch(s -> provider != null && s.equalsIgnoreCase(provider)); + } + public List getSupportedServices() { + if (!isExternalNetworkProvider() && CollectionUtils.isEmpty(supportedServices)) { + throw new InvalidParameterValueException("Supported services needs to be provided"); + } + if (isExternalNetworkProvider()) { + supportedServices = new ArrayList<>(List.of( + Dhcp.getName(), + Dns.getName(), + NetworkACL.getName(), + UserData.getName() + )); + if (NetworkOffering.NetworkMode.NATTED.name().equalsIgnoreCase(getNetworkMode())) { + supportedServices.addAll(Arrays.asList( + StaticNat.getName(), + SourceNat.getName(), + PortForwarding.getName())); + } + if (NetworkOffering.NetworkMode.ROUTED.name().equalsIgnoreCase(getNetworkMode())) { + supportedServices.add(Gateway.getName()); + } + if (getNsxSupportsLbService() || isNetrisNatted(getProvider(), getNetworkMode())) { + supportedServices.add(Lb.getName()); + } + } return supportedServices; } + public String getProvider() { + return provider; + } + + public String getNetworkMode() { + return networkMode; + } + + public boolean getNsxSupportsLbService() { + return org.apache.commons.lang3.BooleanUtils.isTrue(nsxSupportsLbService); + } + public Map> getServiceProviders() { - Map> serviceProviderMap = null; - if (serviceProviderList != null && !serviceProviderList.isEmpty()) { - serviceProviderMap = new HashMap>(); + Map> serviceProviderMap = new HashMap<>(); + if (serviceProviderList != null && !serviceProviderList.isEmpty() && !isExternalNetworkProvider()) { Collection> servicesCollection = serviceProviderList.values(); Iterator> iter = servicesCollection.iterator(); while (iter.hasNext()) { Map obj = iter.next(); - if (logger.isTraceEnabled()) { - logger.trace("service provider entry specified: " + obj); - } + logger.trace("Service provider entry specified: {}", obj); HashMap services = (HashMap)obj; String service = services.get("service"); String provider = services.get("provider"); @@ -144,11 +237,35 @@ public Map> getServiceProviders() { 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) { + List unsupportedServices = new ArrayList<>(List.of("Vpn", "BaremetalPxeService", "SecurityGroup", "Connectivity", "Firewall")); + if (NetworkOffering.NetworkMode.NATTED.name().equalsIgnoreCase(getNetworkMode())) { + unsupportedServices.add("Gateway"); + } + List routerSupported = List.of("Dhcp", "Dns", "UserData"); + List allServices = Network.Service.listAllServices().stream().map(Network.Service::getName).collect(Collectors.toList()); + for (String service : allServices) { + if (unsupportedServices.contains(service)) + continue; + if (routerSupported.contains(service)) + serviceProviderMap.put(service, List.of(VirtualRouterProvider.Type.VPCVirtualRouter.name())); + else if (NetworkOffering.NetworkMode.NATTED.name().equalsIgnoreCase(getNetworkMode()) || + Stream.of(NetworkACL.getName(), Gateway.getName()).anyMatch(s -> s.equalsIgnoreCase(service))) { + serviceProviderMap.put(service, List.of(provider)); + } + } + if ((isNsxWithoutLb(getProvider(), getNsxSupportsLbService())) || isNetrisRouted(getProvider(), getNetworkMode())) { + serviceProviderMap.remove(Lb.getName()); + } + } + public Map> getServiceCapabilityList() { return serviceCapabilityList; } @@ -186,6 +303,14 @@ public Boolean getEnable() { return false; } + public Boolean getSpecifyAsNumber() { + return BooleanUtils.toBoolean(specifyAsNumber); + } + + public String getRoutingMode() { + return routingMode; + } + @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 24660e41ed9b..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 @@ -31,6 +31,8 @@ import com.cloud.dc.DataCenter; import com.cloud.user.Account; +import java.util.List; + @APICommand(name = "createZone", description = "Creates a Zone.", responseObject = ZoneResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateZoneCmd extends BaseCmd { @@ -40,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") @@ -70,24 +72,29 @@ 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, + type = CommandType.LIST, collectionType = CommandType.STRING, + description = "comma separated list of storage access groups for the hosts in the zone", + since = "4.21.0") + private List storageAccessGroups; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -162,6 +169,10 @@ public boolean isEdge() { return isEdge; } + public List getStorageAccessGroups() { + return storageAccessGroups; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// @Override 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..85e7b0af3193 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 @@ -40,7 +40,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 +53,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 +67,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; ///////////////////////////////////////////////////// @@ -111,13 +111,13 @@ public void execute() { 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 +146,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 d38ae057f058..69832d4dfdc9 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 @@ -38,7 +38,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 +50,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 +64,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 +103,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); } ///////////////////////////////////////////////////// @@ -118,7 +118,7 @@ public void execute() { 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 8319911c5c8d..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,10 +78,9 @@ 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 public long getEntityOwnerId() { Project project = _projectService.getProject(projectId); @@ -108,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 11c4d863c47e..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 @@ -20,6 +20,7 @@ import java.util.EnumSet; import java.util.List; +import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -31,13 +32,14 @@ import org.apache.cloudstack.api.response.AccountResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.ResourceIconResponse; +import org.apache.commons.collections.CollectionUtils; import com.cloud.exception.InvalidParameterValueException; import com.cloud.server.ResourceIcon; 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"; @@ -48,31 +50,37 @@ 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") + private String tag; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -116,10 +124,18 @@ public EnumSet getDetails() throws InvalidParameterValueException return dv; } - public Boolean getShowIcon() { + public String getApiKeyAccess() { + return apiKeyAccess; + } + + public boolean getShowIcon() { return showIcon != null ? showIcon : false; } + public String getTag() { + return tag; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -134,12 +150,20 @@ public void execute() { ListResponse response = _queryService.searchForAccounts(this); response.setResponseName(getCommandName()); setResponseObject(response); - if (response != null && response.getCount() > 0 && getShowIcon()) { - updateAccountResponse(response.getResponses()); - } + updateAccountResponse(response.getResponses()); } - private void updateAccountResponse(List response) { + protected void updateAccountResponse(List response) { + if (CollectionUtils.isEmpty(response)) { + return; + } + EnumSet details = getDetails(); + if (details.contains(DomainDetails.all) || details.contains(DomainDetails.resource)) { + _resourceLimitService.updateTaggedResourceLimitsAndCountsForAccounts(response, getTag()); + } + if (!getShowIcon()) { + return; + } for (AccountResponse accountResponse : response) { ResourceIcon resourceIcon = resourceIconManager.getByResourceTypeAndUuid(ResourceTag.ResourceObjectType.Account, accountResponse.getObjectId()); if (resourceIcon == null) { 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 5c1c61130ba6..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 @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.List; +import org.apache.commons.lang.BooleanUtils; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; @@ -49,61 +50,67 @@ 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") + private Boolean forSystemVMs; + + @Parameter(name = ApiConstants.FOR_PROVIDER, type = CommandType.BOOLEAN, description = "true if range is dedicated for external network provider", since = "4.21.0") + private Boolean forProvider; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -175,6 +182,14 @@ public String getState() { return state; } + public boolean getForSystemVMs() { + return BooleanUtils.isTrue(forSystemVMs); + } + + public boolean isForProvider() { + return BooleanUtils.isTrue(forProvider); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/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 f5b8c3da8550..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,10 +97,10 @@ 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." + + "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, since = "4.18.0") @@ -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 e8ca502b5cdb..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,10 +92,10 @@ 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." + + "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, since = "4.18.0") @@ -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..28cd642e1a41 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 @@ -40,7 +40,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 +56,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 +90,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()); @@ -109,6 +109,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 558f92e4006d..ca60ea674fe3 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,9 +57,30 @@ 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, + type = CommandType.STRING, + description = "the name of the backup", + since = "4.21.0") + private String name; + + @Parameter(name = ApiConstants.DESCRIPTION, + type = CommandType.STRING, + description = "the description for the backup", + since = "4.21.0") + private String description; + + @Parameter(name = ApiConstants.QUIESCE_VM, + type = CommandType.BOOLEAN, + required = false, + description = "Quiesce the instance before checkpointing the disks for backup. Applicable only to NAS backup provider. " + + "The filesystem is frozen before the backup starts and thawed immediately after. " + + "Requires the instance to have the QEMU Guest Agent installed and running.", + since = "4.21.0") + private Boolean quiesceVM; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -68,6 +89,18 @@ public Long getVmId() { return vmId; } + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public Boolean getQuiesceVM() { + return quiesceVM; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -75,13 +108,13 @@ public Long getVmId() { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { try { - boolean result = backupManager.createBackup(getVmId()); + boolean result = backupManager.createBackup(this, getJob()); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); 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()); @@ -105,7 +138,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/CreateBackupScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupScheduleCmd.java index 5dc06af21231..67ad7c71503f 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 @@ -26,7 +26,6 @@ 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 +36,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 +52,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; @@ -75,6 +74,19 @@ public class CreateBackupScheduleCmd extends BaseCmd { description = "Specifies a timezone for this command. For more information on the timezone parameter, see TimeZone Format.") private String timezone; + @Parameter(name = ApiConstants.MAX_BACKUPS, type = CommandType.INTEGER, + since = "4.21.0", description = ApiConstants.PARAMETER_DESCRIPTION_MAX_BACKUPS) + private Integer maxBackups; + + @Parameter(name = ApiConstants.QUIESCE_VM, + type = CommandType.BOOLEAN, + required = false, + description = "Quiesce the instance before checkpointing the disks for backup. Applicable only to NAS backup provider. " + + "The filesystem is frozen before the backup starts and thawed immediately after. " + + "Requires the instance to have the QEMU Guest Agent installed and running.", + since = "4.21.0") + private Boolean quiesceVM; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -95,6 +107,14 @@ public String getTimezone() { return timezone; } + public Integer getMaxBackups() { + return maxBackups; + } + + public Boolean getQuiesceVM() { + return quiesceVM; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -108,7 +128,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()); 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 0245f228b895..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 @@ -26,6 +26,7 @@ import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.BackupScheduleResponse; import org.apache.cloudstack.api.response.SuccessResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.backup.BackupManager; @@ -39,10 +40,10 @@ 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 { +public class DeleteBackupScheduleCmd extends BaseCmd { @Inject private BackupManager backupManager; @@ -51,13 +52,15 @@ public class DeleteBackupScheduleCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, - type = CommandType.UUID, - entityType = UserVmResponse.class, - required = true, - description = "ID of the VM") + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, + description = "ID of the VM from which all backup schedules will be deleted.") private Long vmId; + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = BackupScheduleResponse.class, + since = "4.20.1", description = "ID of the backup schedule to be deleted. It has precedence over the 'virtualmachineid' parameter, " + + "i.e., when the 'id' parameter is specified, the 'virtualmachineid' parameter will be ignored.") + private Long id; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -66,6 +69,9 @@ public Long getVmId() { return vmId; } + public Long getId() { return id; } + + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -73,13 +79,13 @@ public Long getVmId() { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { try { - boolean result = backupManager.deleteBackupSchedule(getVmId()); + boolean result = backupManager.deleteBackupSchedule(this); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); response.setResponseName(getCommandName()); 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/ListBackupScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupScheduleCmd.java index 6cc765328f61..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 @@ -19,14 +19,16 @@ import javax.inject.Inject; +import com.amazonaws.util.CollectionUtils; 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.BaseListProjectAndAccountResourcesCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.BackupScheduleResponse; +import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.backup.BackupManager; import org.apache.cloudstack.backup.BackupSchedule; @@ -37,16 +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 ///////////////////// @@ -55,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 /////////////////////// ///////////////////////////////////////////////////// @@ -67,6 +77,10 @@ public Long getVmId() { return vmId; } + public Long getId() { + return id; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -74,14 +88,18 @@ public Long getVmId() { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { try{ - BackupSchedule schedule = backupManager.listBackupSchedule(getVmId()); - if (schedule != null) { - BackupScheduleResponse response = _responseGenerator.createBackupScheduleResponse(schedule); - response.setResponseName(getCommandName()); - setResponseObject(response); - } else { - throw new CloudRuntimeException("No backup schedule exists for the VM"); + 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); } 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/ListBackupsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java index 8597d97278c9..fb9c92f433e5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java @@ -29,6 +29,7 @@ import org.apache.cloudstack.api.BaseListProjectAndAccountResourcesCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.BackupOfferingResponse; import org.apache.cloudstack.api.response.BackupResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.UserVmResponse; @@ -45,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 { @@ -60,21 +61,40 @@ 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, + type = CommandType.STRING, + since = "4.21.0", + description = "list backups by name") + private String name; + + @Parameter(name = ApiConstants.BACKUP_OFFERING_ID, + type = CommandType.UUID, + entityType = BackupOfferingResponse.class, + since = "4.21.0", + description = "list backups by backup offering") + private Long backupOfferingId; + + @Parameter(name = ApiConstants.LIST_VM_DETAILS, + type = CommandType.BOOLEAN, + since = "4.21.0", + description = "list backups with VM details") + private Boolean listVmDetails; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -87,10 +107,22 @@ public Long getVmId() { return vmId; } + public String getName() { + return name; + } + + public Long getBackupOfferingId() { + return backupOfferingId; + } + public Long getZoneId() { return zoneId; } + public Boolean getListVmDetails() { + return listVmDetails; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -101,7 +133,7 @@ protected void setupResponseBackupList(final List backups, final Integer if (backup == null) { continue; } - BackupResponse backupResponse = _responseGenerator.createBackupResponse(backup); + BackupResponse backupResponse = backupManager.createBackupResponse(backup, this.getListVmDetails()); responses.add(backupResponse); } final ListResponse response = new ListResponse<>(); @@ -116,6 +148,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE Pair, Integer> result = backupManager.listBackups(this); setupResponseBackupList(result.first(), result.second()); } catch (Exception 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..dcf9f15b4dc5 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 @@ -39,7 +39,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 +55,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 +87,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()); @@ -106,6 +106,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..c29d117161f2 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 @@ -40,7 +40,7 @@ import com.cloud.utils.exception.CloudRuntimeException; @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 { @@ -80,7 +80,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE 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 +99,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..703a1b2e8802 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 @@ -41,7 +41,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 { @@ -57,7 +57,7 @@ public class RestoreVolumeFromBackupAndAttachToVMCmd 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.VOLUME_ID, @@ -70,7 +70,7 @@ public class RestoreVolumeFromBackupAndAttachToVMCmd extends BaseAsyncCmd { 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; ///////////////////////////////////////////////////// @@ -107,7 +107,7 @@ public void execute() throws ResourceUnavailableException, InsufficientCapacityE 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 +121,6 @@ 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); } } 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 new file mode 100644 index 000000000000..7caa4ce710ff --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/repository/AddBackupRepositoryCmd.java @@ -0,0 +1,144 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 com.cloud.utils.StringUtils; +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.api.response.ZoneResponse; +import org.apache.cloudstack.backup.BackupRepository; +import org.apache.cloudstack.backup.BackupRepositoryService; +import org.apache.cloudstack.context.CallContext; + +import javax.inject.Inject; + +@APICommand(name = "addBackupRepository", + description = "Adds a backup repository to store NAS backups", + responseObject = BackupRepositoryResponse.class, since = "4.20.0", + authorized = {RoleType.Admin}) +public class AddBackupRepositoryCmd extends BaseCmd { + + @Inject + private BackupRepositoryService backupRepositoryService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "name of the backup repository") + private String name; + + @Parameter(name = ApiConstants.ADDRESS, type = CommandType.STRING, required = true, description = "address of the backup repository") + private String address; + + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, required = true, description = "type of the backup repository storage. Supported values: nfs, cephfs, cifs") + private String type; + + @Parameter(name = ApiConstants.PROVIDER, type = CommandType.STRING, description = "backup repository provider") + private String provider; + + @Parameter(name = ApiConstants.MOUNT_OPTIONS, type = CommandType.STRING, description = "shared storage mount options") + private String mountOptions; + + @Parameter(name = ApiConstants.ZONE_ID, + type = CommandType.UUID, + entityType = ZoneResponse.class, + required = true, + 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 /////////////////////// + ///////////////////////////////////////////////////// + + public BackupRepositoryService getBackupRepositoryService() { + return backupRepositoryService; + } + + public String getName() { + return name; + } + + public String getType() { + if ("cephfs".equalsIgnoreCase(type)) { + return "ceph"; + } + return type.toLowerCase(); + } + + public String getAddress() { + return address; + } + + public String getProvider() { + return provider; + } + + public String getMountOptions() { + return StringUtils.isBlank(mountOptions) ? "" : mountOptions; + } + + public Long getZoneId() { + return zoneId; + } + + public Long getCapacityBytes() { + return capacityBytes; + } + + public Boolean crossZoneInstanceCreationEnabled() { + return crossZoneInstanceCreation; + } + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + try { + BackupRepository result = backupRepositoryService.addBackupRepository(this); + if (result != null) { + BackupRepositoryResponse response = _responseGenerator.createBackupRepositoryResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add 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/backup/repository/DeleteBackupRepositoryCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/repository/DeleteBackupRepositoryCmd.java new file mode 100644 index 000000000000..912170eb4ca2 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/repository/DeleteBackupRepositoryCmd.java @@ -0,0 +1,76 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.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.api.response.SuccessResponse; +import org.apache.cloudstack.backup.BackupRepositoryService; + +import javax.inject.Inject; + +@APICommand(name = "deleteBackupRepository", + description = "delete a backup repository", + responseObject = SuccessResponse.class, since = "4.20.0", + authorized = {RoleType.Admin}) +public class DeleteBackupRepositoryCmd extends BaseCmd { + + @Inject + BackupRepositoryService backupRepositoryService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name = ApiConstants.ID, + type = CommandType.UUID, + entityType = BackupRepositoryResponse.class, + required = true, + description = "ID of the backup repository to be deleted") + private Long id; + + + ///////////////////////////////////////////////////// + //////////////// Accessors ////////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + @Override + public void execute() { + boolean result = backupRepositoryService.deleteBackupRepository(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete backup repository"); + } + } + + @Override + public long getEntityOwnerId() { + return 0; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/repository/ListBackupRepositoriesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/repository/ListBackupRepositoriesCmd.java new file mode 100644 index 000000000000..8293afb657d5 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/repository/ListBackupRepositoriesCmd.java @@ -0,0 +1,110 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.user.backup.repository; + +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.Pair; +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.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.BackupRepositoryResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.backup.BackupRepository; +import org.apache.cloudstack.backup.BackupRepositoryService; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +@APICommand(name = "listBackupRepositories", + description = "Lists all backup repositories", + responseObject = BackupRepositoryResponse.class, since = "4.20.0", + authorized = {RoleType.Admin}) +public class ListBackupRepositoriesCmd extends BaseListCmd { + + @Inject + BackupRepositoryService backupRepositoryService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "name of the backup repository") + private String name; + + @Parameter(name = ApiConstants.ZONE_ID, + type = CommandType.UUID, + entityType = ZoneResponse.class, + description = "ID of the zone where the backup repository is to be added") + private Long zoneId; + + @Parameter(name = ApiConstants.PROVIDER, type = CommandType.STRING, description = "the backup repository provider") + private String provider; + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = BackupRepositoryResponse.class, description = "ID of the backup repository") + private Long id; + + ///////////////////////////////////////////////////// + //////////////// Accessors ////////////////////////// + ///////////////////////////////////////////////////// + + + public String getName() { + return name; + } + + public Long getZoneId() { + return zoneId; + } + + public String getProvider() { + return provider; + } + + public Long getId() { + return id; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try { + Pair, Integer> repositoriesPair = backupRepositoryService.listBackupRepositories(this); + List backupRepositories = repositoriesPair.first(); + ListResponse response = new ListResponse<>(); + List responses = new ArrayList<>(); + for (BackupRepository repository : backupRepositories) { + responses.add(_responseGenerator.createBackupRepositoryResponse(repository)); + } + response.setResponses(responses, repositoriesPair.second()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (Exception e) { + String msg = String.format("Error listing backup repositories, due to: %s", e.getMessage()); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, msg); + } + + } +} 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/bgp/ListASNumbersCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/bgp/ListASNumbersCmd.java new file mode 100644 index 000000000000..b835f7225df3 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/bgp/ListASNumbersCmd.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.bgp; + +import com.cloud.bgp.ASNumber; +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.Pair; +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.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ASNRangeResponse; +import org.apache.cloudstack.api.response.ASNumberResponse; +import org.apache.cloudstack.api.response.AccountResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.api.response.VpcResponse; +import org.apache.cloudstack.api.response.ZoneResponse; + +import java.util.ArrayList; +import java.util.List; + +@APICommand(name = "listASNumbers", + description = "List Autonomous Systems Numbers", + responseObject = ASNumberResponse.class, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}, + since = "4.20.0") +public class ListASNumbersCmd extends BaseListCmd { + + @Parameter(name = ApiConstants.ZONE_ID, type = BaseCmd.CommandType.UUID, entityType = ZoneResponse.class, + description = "the zone ID") + private Long zoneId; + + @Parameter(name = ApiConstants.ASN_RANGE_ID, type = BaseCmd.CommandType.UUID, entityType = ASNRangeResponse.class, + description = "the AS Number range ID") + private Long asNumberRangeId; + + @Parameter(name = ApiConstants.AS_NUMBER, type = CommandType.INTEGER, entityType = ASNumberResponse.class, + description = "AS number") + private Integer asNumber; + + @Parameter(name = ApiConstants.IS_ALLOCATED, type = CommandType.BOOLEAN, + description = "to indicate if the AS number is allocated to any network") + private Boolean allocated; + + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, + description = "the network id") + private Long networkId; + + @Parameter(name = ApiConstants.VPC_ID, type = CommandType.UUID, entityType = VpcResponse.class, + description = "the vpc id") + private Long vpcId; + + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, entityType = AccountResponse.class, + description = "account name") + private String account; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, + description = "domain id") + private Long domainId; + + public Long getZoneId() { + return zoneId; + } + + public Long getAsNumberRangeId() { + return asNumberRangeId; + } + + public Boolean getAllocated() { + return allocated; + } + + public Integer getAsNumber() { return asNumber; } + + public Long getNetworkId() { + return networkId; + } + + public String getAccount() { + return account; + } + + public Long getDomainId() { + return domainId; + } + + public Long getVpcId() { + return vpcId; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try { + Pair, Integer> pair = bgpService.listASNumbers(this); + List asNumbers = pair.first(); + ListResponse response = new ListResponse<>(); + List responses = new ArrayList<>(); + for (ASNumber asn : asNumbers) { + responses.add(_responseGenerator.createASNumberResponse(asn)); + } + response.setResponses(responses, pair.second()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (Exception e) { + String msg = String.format("Error listing AS Numbers, due to: %s", e.getMessage()); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, msg); + } + + } +} 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 d2c91e578713..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 @@ -72,7 +72,7 @@ public class CreateBucketCmd extends BaseAsyncCreateCmd implements UserCmd { description = "Id of the Object Storage Pool where bucket is created") private long objectStoragePoolId; - @Parameter(name = ApiConstants.QUOTA, type = CommandType.INTEGER,description = "Bucket Quota in GB") + @Parameter(name = ApiConstants.QUOTA, type = CommandType.INTEGER, required = true, description = "Bucket Quota in GiB") private Integer quota; @Parameter(name = ApiConstants.ENCRYPTION, type = CommandType.BOOLEAN, description = "Enable bucket encryption") @@ -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..4a9a0569c3b8 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 @@ -83,7 +83,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 = _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 8e281b20e915..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 @@ -56,7 +56,7 @@ public class UpdateBucketCmd extends BaseCmd { @Parameter(name = ApiConstants.POLICY, type = CommandType.STRING, description = "Bucket Access Policy") private String policy; - @Parameter(name = ApiConstants.QUOTA, type = CommandType.INTEGER,description = "Bucket Quota in GB") + @Parameter(name = ApiConstants.QUOTA, type = CommandType.INTEGER, description = "Bucket Quota in GiB") private Integer quota; ///////////////////////////////////////////////////// @@ -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 cf25dfaf5b54..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,10 +63,12 @@ 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")); response.setAllowUserViewAllDomainAccounts((Boolean)capabilities.get("allowUserViewAllDomainAccounts")); + response.setAllowUserForceStopVM((Boolean)capabilities.get(ApiConstants.ALLOW_USER_FORCE_STOP_VM)); response.setKubernetesServiceEnabled((Boolean)capabilities.get("kubernetesServiceEnabled")); response.setKubernetesClusterExperimentalFeaturesEnabled((Boolean)capabilities.get("kubernetesClusterExperimentalFeaturesEnabled")); response.setCustomHypervisorDisplayName((String) capabilities.get("customHypervisorDisplayName")); @@ -69,6 +83,16 @@ public void execute() { response.setInstancesStatsUserOnly((Boolean) capabilities.get(ApiConstants.INSTANCES_STATS_USER_ONLY)); response.setInstancesDisksStatsRetentionEnabled((Boolean) capabilities.get(ApiConstants.INSTANCES_DISKS_STATS_RETENTION_ENABLED)); response.setInstancesDisksStatsRetentionTime((Integer) capabilities.get(ApiConstants.INSTANCES_DISKS_STATS_RETENTION_TIME)); + response.setSharedFsVmMinCpuCount((Integer)capabilities.get(ApiConstants.SHAREDFSVM_MIN_CPU_COUNT)); + response.setSharedFsVmMinRamSize((Integer)capabilities.get(ApiConstants.SHAREDFSVM_MIN_RAM_SIZE)); + response.setInstanceLeaseEnabled((Boolean) capabilities.get(ApiConstants.INSTANCE_LEASE_ENABLED)); + response.setExtensionsPath((String)capabilities.get(ApiConstants.EXTENSIONS_PATH)); + response.setDynamicScalingEnabled((Boolean) capabilities.get(ApiConstants.DYNAMIC_SCALING_ENABLED)); + response.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/consoleproxy/ListConsoleSessionsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/consoleproxy/ListConsoleSessionsCmd.java new file mode 100644 index 000000000000..774cd9d59fe7 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/consoleproxy/ListConsoleSessionsCmd.java @@ -0,0 +1,182 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License.import org.apache.cloudstack.context.CallContext; +package org.apache.cloudstack.api.command.user.consoleproxy; + +import org.apache.cloudstack.consoleproxy.ConsoleSession; + +import com.cloud.user.Account; +import com.cloud.user.AccountService; +import com.cloud.user.UserAccount; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.ACL; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.AccountResponse; +import org.apache.cloudstack.api.response.ConsoleSessionResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.UserResponse; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.consoleproxy.ConsoleAccessManager; + +import javax.inject.Inject; +import java.util.Date; + +@APICommand(name = "listConsoleSessions", description = "Lists console sessions.", responseObject = ConsoleSessionResponse.class, + entityType = {ConsoleSession.class}, since = "4.21.0", + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + authorized = {RoleType.Admin, RoleType.DomainAdmin, RoleType.ResourceAdmin, RoleType.User}) +public class ListConsoleSessionsCmd extends BaseListCmd { + @Inject + private AccountService accountService; + + @Inject + private ConsoleAccessManager consoleAccessManager; + + @ACL + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ConsoleSessionResponse.class, description = "The ID of the console session.") + private Long id; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "The domain ID of the account that created the console endpoint.") + private Long domainId; + + @ACL + @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "The ID of the account that created the console endpoint.") + private Long accountId; + + @ACL + @Parameter(name = ApiConstants.USER_ID, type = CommandType.UUID, entityType = UserResponse.class, description = "The ID of the user that created the console endpoint.") + private Long userId; + + @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, authorized = {RoleType.Admin}, description = "Lists console sessions from the specified host.") + private Long hostId; + + @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, description = "Lists console sessions generated from this date onwards. " + + ApiConstants.PARAMETER_DESCRIPTION_START_DATE_POSSIBLE_FORMATS) + private Date startDate; + + @Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, description = "Lists console sessions generated up until this date. " + + ApiConstants.PARAMETER_DESCRIPTION_END_DATE_POSSIBLE_FORMATS) + private Date endDate; + + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, description = "The ID of the virtual machine.") + private Long vmId; + + @Parameter(name = ApiConstants.CONSOLE_ENDPOINT_CREATOR_ADDRESS, type = CommandType.STRING, description = "IP address of the creator of the console endpoint.") + private String consoleEndpointCreatorAddress; + + @Parameter(name = ApiConstants.CLIENT_ADDRESS, type = CommandType.STRING, description = "IP address of the client that accessed the console session.") + private String clientAddress; + + @Parameter(name = ApiConstants.ACTIVE_ONLY, type = CommandType.BOOLEAN, + description = "Lists only active console sessions, defaults to true. Active sessions are the ones that have been acquired and have not been removed.") + private boolean activeOnly = true; + + @Parameter(name = ApiConstants.ACQUIRED, type = CommandType.BOOLEAN, + description = "Lists acquired console sessions, defaults to false. Acquired console sessions are the ones that have been accessed. " + + "The 'activeonly' parameter has precedence over the 'acquired' parameter, i.e., when the 'activeonly' parameter is 'true', the 'acquired' parameter value will be ignored.") + private boolean acquired = false; + + @Parameter(name = ApiConstants.IS_RECURSIVE, type = CommandType.BOOLEAN, + description = "Lists console sessions recursively per domain. If an account ID is informed, only the account's console sessions will be listed. Defaults to false.") + private boolean recursive = false; + + public Long getId() { + return id; + } + + public Long getDomainId() { + return domainId; + } + + public Long getAccountId() { + return accountId; + } + + public Long getUserId() { + return userId; + } + + public Long getHostId() { + return hostId; + } + + public Date getStartDate() { + return startDate; + } + + public Date getEndDate() { + return endDate; + } + + public Long getVmId() { + return vmId; + } + + public String getConsoleEndpointCreatorAddress() { + return consoleEndpointCreatorAddress; + } + + public String getClientAddress() { + return clientAddress; + } + + public boolean isActiveOnly() { + return activeOnly; + } + + public boolean getAcquired() { + return acquired; + } + + public boolean isRecursive() { + return recursive; + } + + @Override + public void execute() { + ListResponse response = consoleAccessManager.listConsoleSessions(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + if (getId() != null) { + ConsoleSession consoleSession = consoleAccessManager.listConsoleSessionById(getId()); + if (consoleSession != null) { + return consoleSession.getAccountId(); + } + } + + if (getAccountId() != null) { + return getAccountId(); + } + + if (getUserId() != null) { + UserAccount userAccount = accountService.getUserAccountById(getUserId()); + if (userAccount != null) { + return userAccount.getAccountId(); + } + } + + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/event/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 b5273c649222..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,43 +36,46 @@ 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") + private String state; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -121,6 +124,10 @@ public boolean getArchived() { return archived != null && archived; } + public String getState() { + return state; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// 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..3fd571b7a479 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()); @@ -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 24b5a78c0855..bc65126f33bd 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 @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.List; +import org.apache.commons.collections.CollectionUtils; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; @@ -40,6 +41,7 @@ import com.cloud.network.IpAddress; import com.cloud.network.rules.FirewallRule; import com.cloud.user.Account; +import com.cloud.utils.StringUtils; import com.cloud.utils.net.NetUtils; @APICommand(name = "createFirewallRule", description = "Creates a firewall rule for a given IP address", responseObject = FirewallResponse.class, entityType = {FirewallRule.class}, @@ -55,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; // /////////////////////////////////////////////////// @@ -94,21 +96,44 @@ public Long getIpAddressId() { return ipAddressId; } + public void setIpAddressId(Long ipAddressId) { + this.ipAddressId = ipAddressId; + } + @Override public String getProtocol() { return protocol.trim(); } + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public Integer getPublicStartPort() { + return publicStartPort; + } + + public void setPublicStartPort(Integer publicStartPort) { + this.publicStartPort = publicStartPort; + } + + public Integer getPublicEndPort() { + return publicEndPort; + } + + public void setPublicEndPort(Integer publicEndPort) { + this.publicEndPort = publicEndPort; + } + @Override public List getSourceCidrList() { - if (cidrlist != null) { + if (CollectionUtils.isNotEmpty(cidrlist) && !(cidrlist.size() == 1 && StringUtils.isBlank(cidrlist.get(0)))) { return cidrlist; } else { - List oneCidrList = new ArrayList(); + List oneCidrList = new ArrayList<>(); oneCidrList.add(NetUtils.ALL_IP4_CIDRS); return oneCidrList; } - } // /////////////////////////////////////////////////// @@ -125,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 @@ -146,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 @@ -246,7 +271,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); } } 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 3545b3d21fb9..056807b9b535 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 @@ -64,37 +64,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,13 +102,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 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; + @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. " + + "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; @@ -116,16 +120,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; // /////////////////////////////////////////////////// @@ -155,11 +159,7 @@ public long getVirtualMachineId() { @Override public List getSourceCidrList() { - if (cidrlist != null) { - throw new InvalidParameterValueException("Parameter cidrList is deprecated; if you need to open firewall " - + "rule for the specific cidr, please refer to createFirewallRule command"); - } - return null; + return sourceCidrList; } public Boolean getOpenFirewall() { @@ -199,7 +199,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()); @@ -332,19 +332,15 @@ public int getDestinationPortEnd() { @Override public void create() { - // cidr list parameter is deprecated - if (cidrlist != null) { - throw new InvalidParameterValueException( - "Parameter cidrList is deprecated; if you need to open firewall rule for the specific cidr, please refer to createFirewallRule command"); - } - 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"); } } + _rulesService.validatePortForwardingSourceCidrList(sourceCidrList); + try { PortForwardingRule result = _rulesService.createPortForwardingRule(this, virtualMachineId, privateIp, getOpenFirewall(), isDisplay()); setEntityId(result.getId()); @@ -363,7 +359,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 3fb66bd861fc..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 @@ -32,37 +32,46 @@ import com.cloud.user.Account; import com.cloud.utils.net.Ip; +import java.util.List; + @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, + type = CommandType.LIST, + collectionType = CommandType.STRING, + 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; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -94,6 +103,10 @@ public Boolean getDisplay() { return display; } + public List getSourceCidrList() { + return sourceCidrList; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -130,7 +143,7 @@ public void checkUuid() { @Override public void execute() { - PortForwardingRule rule = _rulesService.updatePortForwardingRule(getId(), getPrivatePort(), getPrivateEndPort(), getVirtualMachineId(), getVmGuestIp(), getCustomId(), getDisplay()); + PortForwardingRule rule = _rulesService.updatePortForwardingRule(this); FirewallRuleResponse fwResponse = new FirewallRuleResponse(); if (rule != null) { fwResponse = _responseGenerator.createPortForwardingRuleResponse(rule); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gpu/ListGpuCardsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gpu/ListGpuCardsCmd.java new file mode 100644 index 000000000000..b035ecbe71b8 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gpu/ListGpuCardsCmd.java @@ -0,0 +1,96 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.gpu; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.api.response.ListResponse; + +@APICommand(name = "listGpuCards", description = "Lists all available GPU cards", + responseObject = GpuCardResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0") +public class ListGpuCardsCmd extends BaseListCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GpuCardResponse.class, + description = "ID of the GPU card") + private Long id; + + @Parameter(name = ApiConstants.VENDOR_NAME, type = CommandType.STRING, + description = "vendor name of the GPU card") + private String vendorName; + + @Parameter(name = ApiConstants.VENDOR_ID, type = CommandType.STRING, + description = "vendor ID of the GPU card") + private String vendorId; + + @Parameter(name = ApiConstants.DEVICE_ID, type = CommandType.STRING, + description = "device ID of the GPU card") + private String deviceId; + + @Parameter(name = ApiConstants.DEVICE_NAME, type = CommandType.STRING, + description = "device name of the GPU card") + private String deviceName; + + @Parameter(name = ApiConstants.ACTIVE_ONLY, type = CommandType.BOOLEAN, + description = "If true, only GPU cards which have a device will be listed. If false, all GPU cards will be listed.") + private Boolean activeOnly; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public String getVendorName() { + return vendorName; + } + + public String getVendorId() { + return vendorId; + } + + public String getDeviceId() { + return deviceId; + } + + public String getDeviceName() { + return deviceName; + } + + public boolean getActiveOnly() { + return Boolean.TRUE.equals(activeOnly); + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override public void execute() { + ListResponse response = gpuService.listGpuCards(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gpu/ListGpuDevicesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gpu/ListGpuDevicesCmd.java new file mode 100644 index 000000000000..19c920628519 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gpu/ListGpuDevicesCmd.java @@ -0,0 +1,65 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.gpu; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.context.CallContext; + +@APICommand(name = "listGpuDevices", description = "Lists all available GPU devices", + responseView = ResponseObject.ResponseView.Restricted, + responseObject = GpuDeviceResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0", authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class ListGpuDevicesCmd extends BaseListCmd implements UserCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, + description = "the virtual machine ID to which the GPU device is allocated") + private Long vmId; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getVmId() { + return vmId; + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation ////////////////// + /// ////////////////////////////////////////////////// + + @Override + public void execute() { + CallContext.current().setEventDetails("Listing GPU devices"); + ListResponse response = gpuService.listGpuDevices(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gpu/ListVgpuProfilesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gpu/ListVgpuProfilesCmd.java new file mode 100644 index 000000000000..85bf91d7aeea --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gpu/ListVgpuProfilesCmd.java @@ -0,0 +1,80 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.gpu; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.VgpuProfileResponse; + +@APICommand(name = "listVgpuProfiles", description = "Lists all available vGPU profiles", + responseObject = VgpuProfileResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0") +public class ListVgpuProfilesCmd extends BaseListCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VgpuProfileResponse.class, + description = "ID of the vGPU profile") + private Long id; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "name of the vGPU profile") + private String name; + + @Parameter(name = ApiConstants.GPU_CARD_ID, type = CommandType.UUID, entityType = GpuCardResponse.class, + description = "the GPU card ID associated with this GPU device") + private Long cardId; + + @Parameter(name = ApiConstants.ACTIVE_ONLY, type = CommandType.BOOLEAN, + description = "If true, only vGPU profiles which have a device will be listed. If false, all vGPU profiles will be listed.") + private Boolean activeOnly; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public Long getCardId() { + return cardId; + } + + public boolean getActiveOnly() { + return Boolean.TRUE.equals(activeOnly); + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override public void execute() { + ListResponse response = gpuService.listVgpuProfiles(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCategoriesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCategoriesCmd.java index c74514d662ca..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 @@ -26,7 +26,9 @@ import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.response.GuestOSCategoryResponse; import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import com.cloud.cpu.CPU; import com.cloud.storage.GuestOsCategory; import com.cloud.utils.Pair; @@ -39,12 +41,48 @@ public class ListGuestOsCategoriesCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOSCategoryResponse.class, description = "list Os category by id") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOSCategoryResponse.class, description = "List OS category by ID") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "list os category by name", since = "3.0.1") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "List OS category by name", since = "3.0.1") private String name; + @Parameter(name = ApiConstants.IS_FEATURED, + type = CommandType.BOOLEAN, + description = "List available OS categories by featured or not", + since = "4.21.0") + private Boolean featured; + + @Parameter(name = ApiConstants.IS_ISO, + type = CommandType.BOOLEAN, + description = "List OS categories for which an ISO is available", + since = "4.21.0") + private Boolean iso; + + @Parameter(name = ApiConstants.IS_VNF, type = CommandType.BOOLEAN, + description = "List OS categories for which a VNF template is available", + since = "4.21.0") + private Boolean vnf; + + @Parameter(name = ApiConstants.ZONE_ID, + type = CommandType.UUID, + entityType = ZoneResponse.class, + description = "List available OS categories types for the zone", + since = "4.21.0") + private Long zoneId; + + @Parameter(name = ApiConstants.ARCH, + type = CommandType.STRING, + description = "List OS categories types available for given CPU architecture", + since = "4.21.0") + private String arch; + + @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, + type = CommandType.BOOLEAN, + description = "flag to display the resource image for the OS category", + since = "4.21.0") + private Boolean showIcon; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -57,6 +95,30 @@ public String getName() { return name; } + public Boolean isFeatured() { + return featured; + } + + public Boolean isIso() { + return iso; + } + + public Boolean isVnf() { + return vnf; + } + + public Long getZoneId() { + return zoneId; + } + + public CPU.CPUArch getArch() { + return arch == null ? null : CPU.CPUArch.fromType(arch); + } + + public boolean isShowIcon() { + return Boolean.TRUE.equals(showIcon); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -64,14 +126,11 @@ public String getName() { @Override public void execute() { Pair, Integer> result = _mgr.listGuestOSCategoriesByCriteria(this); - ListResponse response = new ListResponse(); - List osCatResponses = new ArrayList(); + ListResponse response = new ListResponse<>(); + List osCatResponses = new ArrayList<>(); for (GuestOsCategory osCategory : result.first()) { - GuestOSCategoryResponse categoryResponse = new GuestOSCategoryResponse(); - categoryResponse.setId(osCategory.getUuid()); - categoryResponse.setName(osCategory.getName()); - - categoryResponse.setObjectName("oscategory"); + GuestOSCategoryResponse categoryResponse = _responseGenerator.createGuestOSCategoryResponse(osCategory, + isShowIcon()); osCatResponses.add(categoryResponse); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCmd.java index b31a46692201..4ff2ebf0a667 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,13 @@ 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.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", diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/CreateGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/CreateGuiThemeCmd.java new file mode 100644 index 000000000000..8566b413cc12 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/CreateGuiThemeCmd.java @@ -0,0 +1,129 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.gui.theme; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuiThemeResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.gui.theme.GuiTheme; +import org.apache.cloudstack.gui.theme.GuiThemeJoin; +import org.apache.cloudstack.gui.theme.GuiThemeService; + +import javax.inject.Inject; + +@APICommand(name = "createGuiTheme", description = "Creates a customized GUI theme for a set of Common Names (fixed or wildcard), a set of domain UUIDs, and/or a set of " + + "account UUIDs.", responseObject = GuiThemeResponse.class, entityType = {GuiTheme.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0.0", authorized = {RoleType.Admin}) +public class CreateGuiThemeCmd extends BaseCmd { + + @Inject + GuiThemeService guiThemeService; + + @Parameter(name = ApiConstants.NAME, required = true, type = CommandType.STRING, length = 2048, description = "A name to identify the theme.") + private String name; + + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, length = 4096, description = "A description for the theme.") + private String description; + + @Parameter(name = ApiConstants.CSS, type = CommandType.STRING, length = 65535, description = "The CSS to be retrieved and imported into the GUI " + + "when matching the theme access configurations.") + private String css; + + @Parameter(name = ApiConstants.JSON_CONFIGURATION, type = CommandType.STRING, length = 65535, description = "The JSON with the configurations to be " + + "retrieved and imported into the GUI when matching the theme access configurations.") + private String jsonConfiguration; + + @Parameter(name = ApiConstants.COMMON_NAMES, type = CommandType.STRING, length = 65535, description = "A set of Common Names (CN) (fixed or " + + "wildcard) separated by comma that can retrieve the theme; e.g.: *acme.com,acme2.com") + private String commonNames; + + @Parameter(name = ApiConstants.DOMAIN_IDS, type = CommandType.STRING, length = 65535, description = "A set of domain UUIDs (also known as ID for " + + "the end-user) separated by comma that can retrieve the theme.") + private String domainIds; + + @Parameter(name = ApiConstants.ACCOUNT_IDS, type = CommandType.STRING, length = 65535, description = "A set of account UUIDs (also known as ID for" + + " the end-user) separated by comma that can retrieve the theme.") + private String accountIds; + + @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "Defines whether a theme can be retrieved by anyone when only " + + "the `commonNames` is informed. If the `domainIds` or `accountIds` is informed, it is considered as `false`.") + private Boolean isPublic = true; + + @Parameter(name = ApiConstants.RECURSIVE_DOMAINS, type = CommandType.BOOLEAN, description = "Defines whether the subdomains of the informed domains are considered. Default " + + "value is false.") + private Boolean recursiveDomains = false; + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public String getCss() { + return css; + } + + public String getJsonConfiguration() { + return jsonConfiguration; + } + + public String getCommonNames() { + return commonNames; + } + + public String getDomainIds() { + return domainIds; + } + + public String getAccountIds() { + return accountIds; + } + + public Boolean getPublic() { + return isPublic; + } + + public Boolean getRecursiveDomains() { + return recursiveDomains; + } + + @Override + public void execute() { + GuiThemeJoin guiThemeJoin = guiThemeService.createGuiTheme(this); + + if (guiThemeJoin == null) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create the GUI theme."); + } + + GuiThemeResponse response = _responseGenerator.createGuiThemeResponse(guiThemeJoin); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/ListGuiThemesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/ListGuiThemesCmd.java new file mode 100644 index 000000000000..35a0a749aa94 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/ListGuiThemesCmd.java @@ -0,0 +1,110 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.gui.theme; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.AccountResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.GuiThemeResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.gui.theme.GuiTheme; +import org.apache.cloudstack.gui.theme.GuiThemeService; + +import javax.inject.Inject; + +@APICommand(name = "listGuiThemes", description = "Lists GUI themes.", responseObject = GuiThemeResponse.class, entityType = {GuiTheme.class}, + since = "4.21.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin, RoleType.User, RoleType.DomainAdmin, + RoleType.ResourceAdmin}) +public class ListGuiThemesCmd extends BaseListCmd { + + @Inject + GuiThemeService guiThemeService; + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, description = "The theme ID.", entityType = GuiThemeResponse.class) + private Long id; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the theme.") + private String name; + + @Parameter(name = ApiConstants.COMMON_NAME, type = CommandType.STRING, description = "The internet Common Name (CN) to be filtered.") + private String commonName; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "The ID of the domain to be filtered.") + private Long domainId; + + @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "The ID of the account to be filtered.") + private Long accountId; + + @Parameter(name = ApiConstants.LIST_ALL, type = CommandType.BOOLEAN, description = "Whether to list all themes.") + private boolean listAll = false; + + @Parameter(name = ApiConstants.SHOW_REMOVED, type = CommandType.BOOLEAN, description = "Whether to list removed themes.") + private boolean showRemoved = false; + + @Parameter(name = ApiConstants.SHOW_PUBLIC, type = CommandType.BOOLEAN, description = "Whether to list public themes.") + private Boolean showPublic; + + @Parameter(name = ApiConstants.LIST_ONLY_DEFAULT_THEME, type = CommandType.BOOLEAN, description = "Whether to only list the default theme.") + private boolean listOnlyDefaultTheme = false; + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getCommonName() { + return commonName; + } + + public Long getDomainId() { + return domainId; + } + + public Long getAccountId() { + return accountId; + } + + public boolean getListAll() { + return listAll; + } + + public boolean getShowRemoved() { + return showRemoved; + } + + public Boolean getShowPublic() { + return showPublic; + } + + public boolean getListOnlyDefaultTheme() { + return listOnlyDefaultTheme; + } + + @Override + public void execute() { + ListResponse response = guiThemeService.listGuiThemes(this); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/RemoveGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/RemoveGuiThemeCmd.java new file mode 100644 index 000000000000..64164838eba4 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/RemoveGuiThemeCmd.java @@ -0,0 +1,60 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.gui.theme; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.GuiThemeResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.gui.theme.GuiTheme; +import org.apache.cloudstack.gui.theme.GuiThemeService; + +import javax.inject.Inject; + +@APICommand(name = "removeGuiTheme", description = "Removes an existing GUI theme.", responseObject = GuiThemeResponse.class, entityType = {GuiTheme.class}, + since = "4.21.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin}) +public class RemoveGuiThemeCmd extends BaseCmd { + + @Inject + GuiThemeService guiThemeService; + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuiThemeResponse.class, required = true, + description = "The unique identifier of the GUI theme to be removed.") + private Long id; + + public Long getId() { + return id; + } + + @Override + public void execute() { + guiThemeService.removeGuiTheme(this); + final SuccessResponse response = new SuccessResponse(); + response.setResponseName(getCommandName()); + response.setSuccess(true); + setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/UpdateGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/UpdateGuiThemeCmd.java new file mode 100644 index 000000000000..daef2235ce89 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/UpdateGuiThemeCmd.java @@ -0,0 +1,136 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.gui.theme; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuiThemeResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.gui.theme.GuiTheme; +import org.apache.cloudstack.gui.theme.GuiThemeJoin; +import org.apache.cloudstack.gui.theme.GuiThemeService; + +import javax.inject.Inject; + + +@APICommand(name = "updateGuiTheme", description = "Updates an existing GUI theme.", responseObject = GuiThemeResponse.class, entityType = {GuiTheme.class}, + since = "4.21.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin}) +public class UpdateGuiThemeCmd extends BaseCmd { + + @Inject + GuiThemeService guiThemeService; + + @Parameter(name = ApiConstants.ID, required = true, type = CommandType.UUID, entityType = GuiThemeResponse.class, description = "The ID of the theme to be updated.") + private Long id; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, length = 2048, description = "A name to identify the theme.") + private String name; + + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, length = 4096, description = "A description for the theme.") + private String description; + + @Parameter(name = ApiConstants.CSS, type = CommandType.STRING, length = 65535, description = "The CSS to be retrieved and imported into the GUI " + + "when matching the theme access configurations.") + private String css; + + @Parameter(name = ApiConstants.JSON_CONFIGURATION, type = CommandType.STRING, length = 65535, description = "The JSON with the configurations to be " + + "retrieved and imported into the GUI when matching the theme access configurations.") + private String jsonConfiguration; + + @Parameter(name = ApiConstants.COMMON_NAMES, type = CommandType.STRING, length = 65535, description = "A set of Common Names (CN) (fixed or " + + "wildcard) separated by comma that can retrieve the theme; e.g.: *acme.com,acme2.com") + private String commonNames; + + @Parameter(name = ApiConstants.DOMAIN_IDS, type = CommandType.STRING, length = 65535, description = "A set of domain UUIDs (also known as ID for " + + "the end-user) separated by comma that can retrieve the theme.") + private String domainIds; + + @Parameter(name = ApiConstants.RECURSIVE_DOMAINS, type = CommandType.BOOLEAN, description = "Defines whether the subdomains of the informed domains are considered. Default " + + "value is false.") + private Boolean recursiveDomains = false; + + @Parameter(name = ApiConstants.ACCOUNT_IDS, type = CommandType.STRING, length = 65535, description = "A set of account UUIDs (also known as ID for" + + " the end-user) separated by comma that can retrieve the theme.") + private String accountIds; + + @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "Defines whether a theme can be retrieved by anyone when only " + + "the `commonNames` is informed. If the `domainIds` or `accountIds` is informed, it is considered as `false`.") + private Boolean isPublic = true; + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public String getCss() { + return css; + } + + public String getJsonConfiguration() { + return jsonConfiguration; + } + + public String getCommonNames() { + return commonNames; + } + + public String getDomainIds() { + return domainIds; + } + + public String getAccountIds() { + return accountIds; + } + + public Boolean getRecursiveDomains() { + return recursiveDomains; + } + + public Boolean getIsPublic() { + return isPublic; + } + + @Override + public void execute() { + GuiThemeJoin guiThemeJoin = guiThemeService.updateGuiTheme(this); + + if (guiThemeJoin == null) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update the GUI theme."); + } + + GuiThemeResponse response = _responseGenerator.createGuiThemeResponse(guiThemeJoin); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/ipv6/CreateIpv6FirewallRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/ipv6/CreateIpv6FirewallRuleCmd.java index 4e3cf4621efd..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 has to 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 292e1c6f099b..cf4aa41f795c 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 @@ -33,7 +33,7 @@ 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,7 +44,7 @@ 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, @@ -78,7 +78,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 +89,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 +104,7 @@ public ApiCommandResourceType getApiResourceType() { @Override public void execute() { - boolean result = _templateService.detachIso(virtualMachineId, isForced()); + boolean result = _templateService.detachIso(virtualMachineId, null, isForced()); if (result) { UserVm userVm = _entityMgr.findById(UserVm.class, virtualMachineId); UserVmResponse response = _responseGenerator.createUserVmResponse(getResponseView(), "virtualmachine", userVm).get(0); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ExtractIsoCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ExtractIsoCmd.java index 5db680066a6f..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 @@ -43,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; ///////////////////////////////////////////////////// @@ -101,7 +101,13 @@ public long getEntityOwnerId() { @Override public String getEventDescription() { - return "extracting ISO: " + getId() + " from zone: " + getZoneId(); + String description = "Extracting ISO: " + getResourceUuid(ApiConstants.ID); + + if (getZoneId() == null) { + description += "from zone: " + getResourceUuid(ApiConstants.ZONE_ID); + } + + return description; } @Override @@ -120,7 +126,7 @@ public void execute() { CallContext.current().setEventDetails(getEventDescription()); String uploadUrl = _templateService.extract(this); if (uploadUrl != null) { - ExtractResponse response = _responseGenerator.createExtractResponse(id, zoneId, getEntityOwnerId(), mode, uploadUrl); + ExtractResponse response = _responseGenerator.createImageExtractResponse(id, zoneId, getEntityOwnerId(), mode, uploadUrl); response.setResponseName(getCommandName()); response.setObjectName("iso"); this.setResponseObject(response); 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 04dcbf8ca964..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 @@ -16,10 +16,6 @@ // under the License. package org.apache.cloudstack.api.command.user.iso; -import com.cloud.server.ResourceIcon; -import com.cloud.server.ResourceTag; -import org.apache.cloudstack.api.response.ResourceIconResponse; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -27,16 +23,18 @@ import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.GuestOSCategoryResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.TemplateResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.commons.lang3.StringUtils; +import com.cloud.cpu.CPU; +import com.cloud.server.ResourceTag; import com.cloud.template.VirtualMachineTemplate.TemplateFilter; import com.cloud.user.Account; -import java.util.List; - @APICommand(name = "listIsos", description = "Lists all available ISO files.", responseObject = TemplateResponse.class, responseView = ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListIsosCmd extends BaseListTaggedResourcesCmd implements UserCmd { @@ -47,47 +45,57 @@ 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 = "true if this ISO is 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, s390x", + since = "4.20") + private String arch; + + @Parameter(name = ApiConstants.OS_CATEGORY_ID, type = CommandType.UUID, entityType= GuestOSCategoryResponse.class, + description = "the ID of the OS category for the ISO", + since = "4.21.0") + private Long osCategoryId; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -159,6 +167,17 @@ public boolean listInReadyState() { return onlyReady; } + public CPU.CPUArch getArch() { + if (StringUtils.isBlank(arch)) { + return null; + } + return CPU.CPUArch.fromType(arch); + } + + public Long getOsCategoryId() { + return osCategoryId; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -176,24 +195,14 @@ public ApiCommandResourceType getApiResourceType() { @Override public void execute() { ListResponse response = _queryService.listIsos(this); - if (response != null && response.getCount() > 0 && getShowIcon()) { - updateIsoResponse(response.getResponses()); + if (response != null && getShowIcon()) { + _responseGenerator.updateTemplateIsoResponsesForIcons(response.getResponses(), + ResourceTag.ResourceObjectType.ISO); } response.setResponseName(getCommandName()); setResponseObject(response); } - private void updateIsoResponse(List response) { - for (TemplateResponse templateResponse : response) { - ResourceIcon resourceIcon = resourceIconManager.getByResourceTypeAndUuid(ResourceTag.ResourceObjectType.ISO, templateResponse.getId()); - if (resourceIcon == null) { - continue; - } - ResourceIconResponse iconResponse = _responseGenerator.createResourceIconResponse(resourceIcon); - templateResponse.setResourceIconResponse(iconResponse); - } - } - public Long getStoragePoolId() { return null; }; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java index becfdcd653d3..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 @@ -18,6 +18,7 @@ import java.util.List; +import com.cloud.cpu.CPU; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -49,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") @@ -105,19 +106,24 @@ 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, s390x", + since = "4.20") + private String arch; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -229,6 +235,14 @@ public boolean isPasswordEnabled() { return passwordEnabled == null ? false : passwordEnabled; } + public void setArch(String arch) { + this.arch = arch; + } + + public CPU.CPUArch getArch() { + return CPU.CPUArch.fromType(arch); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -240,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..b55d1b234f19 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 @@ -26,7 +26,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 { 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..93a443757212 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 @@ -34,7 +34,7 @@ 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, required = true, description = "The ID of the asynchronous job") private Long id; ///////////////////////////////////////////////////// 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 4f9d2f37d13f..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 @@ -27,6 +27,7 @@ import org.apache.cloudstack.api.response.FirewallRuleResponse; import org.apache.cloudstack.api.response.SslCertResponse; import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.commons.lang3.BooleanUtils; import com.cloud.event.EventTypes; import com.cloud.exception.ConcurrentOperationException; @@ -47,21 +48,27 @@ 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, + type = CommandType.BOOLEAN, + since = "4.22", + description = "Force assign the certificate. If there is a certificate assigned to the LB, it will be removed at first.") + private Boolean forced; + @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { //To change body of implemented methods use File | Settings | File Templates. - if (_lbService.assignCertToLoadBalancer(getLbRuleId(), getCertId())) { + if (_lbService.assignCertToLoadBalancer(getLbRuleId(), getCertId(), isForced())) { SuccessResponse response = new SuccessResponse(getCommandName()); this.setResponseObject(response); } else { @@ -76,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 @@ -95,4 +102,19 @@ public Long getCertId() { public Long getLbRuleId() { return lbRuleId; } + + public boolean isForced() { + return BooleanUtils.toBoolean(forced); + } + + @Override + public String getSyncObjType() { + return BaseAsyncCmd.networkSyncObject; + } + + @Override + public Long getSyncObjId() { + LoadBalancer lb = _entityMgr.findById(LoadBalancer.class, getLbRuleId()); + return (lb != null)? lb.getNetworkId(): null; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/AssignToLoadBalancerRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/AssignToLoadBalancerRuleCmd.java index 81a52ce2dfec..6d8d356cea4d 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 @@ -45,7 +45,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 +60,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].ip=10.1.1.75", + description = "VM ID and IP map, vmidipmap[0].vmid=1 vmidipmap[0].vmip=10.1.1.75", since = "4.4") private Map vmIdIpMap; @@ -112,7 +112,7 @@ 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(), ",") + ")"; } @@ -128,7 +128,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); } //check whether the given ip is valid ip or not @@ -155,7 +155,7 @@ public Map> getVmIdIpListMap() { @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(); boolean result = false; 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 f86d1ae85dab..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 @@ -33,6 +33,7 @@ import org.apache.cloudstack.api.response.NetworkResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.commons.lang3.StringUtils; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter.NetworkType; @@ -57,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 CIDR list to allow traffic, " + @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 or tcp-proxy.") + @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, description = "The protocol for the LB such as TCP, UDP, TCP-proxy or SSL.") private String lbProtocol; - @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the rule to the end user or not", since = "4.4", authorized = {RoleType.Admin}) + @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; ///////////////////////////////////////////////////// @@ -206,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(); } @@ -216,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"); } } } @@ -253,7 +254,7 @@ public List getSourceCidrList() { } public String getLbProtocol() { - return lbProtocol; + return StringUtils.trim(StringUtils.lowerCase(lbProtocol)); } ///////////////////////////////////////////////////// @@ -267,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()); @@ -339,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"); } } @@ -387,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 dfaafe89923b..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 @@ -82,4 +82,15 @@ public long getEntityOwnerId() { public Long getLbRuleId() { return this.lbRuleId; } + + @Override + public String getSyncObjType() { + return BaseAsyncCmd.networkSyncObject; + } + + @Override + public Long getSyncObjId() { + LoadBalancer lb = _entityMgr.findById(LoadBalancer.class, getLbRuleId()); + return (lb != null)? lb.getNetworkId(): null; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/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..7963dfe5c7d3 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; } 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 127661b1820a..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; @@ -113,9 +113,13 @@ public String getProtocol() { return p; } + public void setProtocol(String protocol) { + this.protocol = 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); @@ -136,6 +140,9 @@ public NetworkACLItem.TrafficType getTrafficType() { throw new InvalidParameterValueException("Invalid traffic type " + trafficType); } + public void setTrafficType(String trafficType) { + this.trafficType = trafficType; + } // /////////////////////////////////////////////////// // ///////////// API Implementation/////////////////// // /////////////////////////////////////////////////// @@ -144,15 +151,23 @@ public String getAction() { return action; } + public void setAction(String action) { + this.action = action; + } + public Integer getNumber() { return number; } - public Integer getSourcePortStart() { + public Integer getPublicStartPort() { return publicStartPort; } - public Integer getSourcePortEnd() { + public void setPublicStartPort(Integer publicStartPort) { + this.publicStartPort = publicStartPort; + } + + public Integer getPublicEndPort() { if (publicEndPort == null) { if (publicStartPort != null) { return publicStartPort; @@ -164,10 +179,18 @@ public Integer getSourcePortEnd() { return null; } + public void setPublicEndPort(Integer publicEndPort) { + this.publicEndPort = publicEndPort; + } + public Long getNetworkId() { return networkId; } + public void setNetworkId(Long networkId) { + this.networkId = networkId; + } + @Override public long getEntityOwnerId() { Account caller = CallContext.current().getCallingAccount(); @@ -207,10 +230,38 @@ public Long getACLId() { return aclId; } + public void setAclId(Long aclId) { + this.aclId = aclId; + } + 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); @@ -223,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 2395339a477c..cbf6df081b3b 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, @@ -191,6 +191,14 @@ public class CreateNetworkCmd extends BaseCmd implements UserCmd { since = "4.19") private String sourceNatIP; + @Parameter(name = ApiConstants.CIDR_SIZE, type = CommandType.INTEGER, + description = "the CIDR size of IPv4 network. For regular users, this is required for isolated networks with ROUTED mode.", + since = "4.20.0") + private Integer cidrSize; + + @Parameter(name=ApiConstants.AS_NUMBER, type=CommandType.LONG, since = "4.20.0", description="the AS Number of the network") + private Long asNumber; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -364,6 +372,10 @@ public String getIp6Cidr() { return NetUtils.standardizeIp6Cidr(ip6Cidr); } + public Integer getCidrSize() { + return cidrSize; + } + public Long getAclId() { return aclId; } @@ -391,6 +403,10 @@ public String getIp6Dns2() { return ip6Dns2; } + public Long getAsNumber() { + return asNumber; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -401,7 +417,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 33f452008d99..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,77 +39,83 @@ 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, + type = CommandType.STRING, + description = "the routing mode for the network offering. Supported types are: Static or Dynamic.", + since = "4.20.0") + private String routingMode; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -186,6 +192,8 @@ public Boolean getForVpc() { return forVpc; } + public String getRoutingMode() { return routingMode; } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// 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..2e638f1e2f76 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,16 +90,16 @@ 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") 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 new file mode 100644 index 000000000000..85166f5ab84a --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/CreateRoutingFirewallRuleCmd.java @@ -0,0 +1,271 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.routing; + +import java.util.ArrayList; +import java.util.List; + +import com.cloud.exception.NetworkRuleConflictException; +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.BaseAsyncCreateCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.FirewallResponse; +import org.apache.cloudstack.api.response.FirewallRuleResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.commons.lang3.StringUtils; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.network.rules.FirewallRule; +import com.cloud.user.Account; +import com.cloud.utils.net.NetUtils; + +@APICommand(name = "createRoutingFirewallRule", + description = "Creates a routing firewall rule in the given network in ROUTED mode", + since = "4.20.0", + responseObject = FirewallRuleResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class CreateRoutingFirewallRuleCmd extends BaseAsyncCreateCmd { + + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, required = true, description = "the protocol for the 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 firewall rule") + private Integer publicStartPort; + + @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 source CIDR list to allow traffic from. Multiple entries must be separated by a single comma character (,).") + protected 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 (,).") + protected List destinationCidrlist; + + @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") + private Integer icmpCode; + + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, + description = "The network of the VM the 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 Routing 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}) + private Boolean display; + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + @Override + public boolean isDisplay() { + if (display != null) { + return display; + } else { + return true; + } + } + + public String getProtocol() { + String p = protocol == null ? "" : protocol.trim(); + if (StringUtils.isNumeric(p)) { + int protoNumber = Integer.parseInt(p); + switch (protoNumber) { + case 1: + p = NetUtils.ICMP_PROTO; + break; + case 6: + p = NetUtils.TCP_PROTO; + break; + case 17: + p = NetUtils.UDP_PROTO; + break; + default: + throw new InvalidParameterValueException(String.format("Protocol %d not supported", protoNumber)); + + } + } + return p; + } + + public List getSourceCidrList() { + if (sourceCidrList != null) { + return sourceCidrList; + } else { + List oneCidrList = new ArrayList(); + oneCidrList.add(NetUtils.ALL_IP4_CIDRS); + return oneCidrList; + } + } + + public List getDestinationCidrList() { + if (destinationCidrlist != null) { + return destinationCidrlist; + } else { + List oneCidrList = new ArrayList(); + oneCidrList.add(NetUtils.ALL_IP4_CIDRS); + return oneCidrList; + } + } + + public FirewallRule.TrafficType getTrafficType() { + if (trafficType == null) { + return FirewallRule.TrafficType.Ingress; + } + for (FirewallRule.TrafficType type : FirewallRule.TrafficType.values()) { + if (type.toString().equalsIgnoreCase(trafficType)) { + return type; + } + } + throw new InvalidParameterValueException("Invalid traffic type " + trafficType); + } + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + public Integer getSourcePortStart() { + return publicStartPort; + } + + public Integer getSourcePortEnd() { + if (publicEndPort == null) { + if (publicStartPort != null) { + return publicStartPort; + } + } else { + return publicEndPort; + } + + return null; + } + + public Long getNetworkId() { + return networkId; + } + + @Override + public long getEntityOwnerId() { + Network network = _networkService.getNetwork(networkId); + if (network != null) { + return network.getAccountId(); + } + Account owner = CallContext.current().getCallingAccount(); + return owner.getAccountId(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_ROUTING_IPV4_FIREWALL_RULE_CREATE; + } + + @Override + public String getEventDescription() { + return "Creating ipv4 firewall rule for routed network"; + } + + public Integer getIcmpCode() { + if (icmpCode != null) { + return icmpCode; + } else if (getProtocol().equalsIgnoreCase(NetUtils.ICMP_PROTO)) { + return -1; + } + return null; + } + + public Integer getIcmpType() { + if (icmpType != null) { + return icmpType; + } else if (getProtocol().equalsIgnoreCase(NetUtils.ICMP_PROTO)) { + return -1; + + } + return null; + } + + @Override + public void create() { + try { + FirewallRule result = routedIpv4Manager.createRoutingFirewallRule(this); + setEntityId(result.getId()); + setEntityUuid(result.getUuid()); + } catch (NetworkRuleConflictException e) { + logger.trace("Network Rule Conflict: ", e); + throw new ServerApiException(ApiErrorCode.NETWORK_RULE_CONFLICT_ERROR, e.getMessage(), e); + } + } + + @Override + public void execute() throws ResourceUnavailableException { + boolean success = false; + FirewallRule rule = _firewallService.getFirewallRule(getEntityId()); + try { + CallContext.current().setEventDetails("Rule ID: " + getEntityUuid()); + success = routedIpv4Manager.applyRoutingFirewallRule(rule.getId()); + + // State is different after the rule is applied, so get new object here + rule = _firewallService.getFirewallRule(getEntityId()); + FirewallResponse ruleResponse = new FirewallResponse(); + if (rule != null) { + ruleResponse = _responseGenerator.createFirewallResponse(rule); + setResponseObject(ruleResponse); + } + ruleResponse.setResponseName(getCommandName()); + } catch (Exception ex) { + logger.error("Got exception when create Routing firewall rules: ", ex); + } finally { + if (!success || rule == null) { + routedIpv4Manager.revokeRoutingFirewallRule(getEntityId()); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Routing firewall rule"); + } + } + } + + @Override + public Long getApiResourceId() { + return getNetworkId(); + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.Network; + } +} 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 new file mode 100644 index 000000000000..646b704e088c --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/DeleteRoutingFirewallRuleCmd.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.network.routing; + +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.FirewallRuleResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; + +import com.cloud.event.EventTypes; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.rules.FirewallRule; +import com.cloud.user.Account; + +@APICommand(name = "deleteRoutingFirewallRule", + description = "Deletes a routing firewall rule", + since = "4.20.0", + responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class DeleteRoutingFirewallRuleCmd extends BaseAsyncCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, description = "the ID of the Routing firewall rule") + private Long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + @Override + public String getEventType() { + return EventTypes.EVENT_ROUTING_IPV4_FIREWALL_RULE_DELETE; + } + + @Override + public String getEventDescription() { + return String.format("Deleting IPv4 routing firewall rule with ID: %s", getResourceUuid(ApiConstants.ID)); + } + + @Override + public long getEntityOwnerId() { + FirewallRule rule = _firewallService.getFirewallRule(id); + if (rule != null) { + return rule.getAccountId(); + } + Account caller = CallContext.current().getCallingAccount(); + return caller.getAccountId(); + } + + @Override + public void execute() throws ResourceUnavailableException { + CallContext.current().setEventDetails("Routing firewall rule with ID: " + getResourceUuid(ApiConstants.ID)); + boolean result = routedIpv4Manager.revokeRoutingFirewallRule(id); + + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete Routing firewall rule"); + } + } + + @Override + public Long getApiResourceId() { + FirewallRule rule = _firewallService.getFirewallRule(id); + if (rule != null) { + return rule.getNetworkId(); + } + return null; + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.Network; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/ListRoutingFirewallRulesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/ListRoutingFirewallRulesCmd.java new file mode 100644 index 000000000000..3fdf3b0f5b46 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/ListRoutingFirewallRulesCmd.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.network.routing; + +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.ApiConstants; +import org.apache.cloudstack.api.BaseListTaggedResourcesCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.command.user.firewall.IListFirewallRulesCmd; +import org.apache.cloudstack.api.response.FirewallResponse; +import org.apache.cloudstack.api.response.FirewallRuleResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.NetworkResponse; + +import com.cloud.network.rules.FirewallRule; +import com.cloud.utils.Pair; + +@APICommand(name = "listRoutingFirewallRules", + description = "Lists all Routing firewall rules", + since = "4.20.0", + responseObject = FirewallRuleResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class ListRoutingFirewallRulesCmd extends BaseListTaggedResourcesCmd implements IListFirewallRulesCmd { + + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, + description = "Lists Routing firewall rule with the specified ID") + private Long id; + + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "list Routing firewall rules by network ID") + private Long networkId; + + @Parameter(name = ApiConstants.TRAFFIC_TYPE, type = CommandType.STRING, description = "list Routing 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}) + private Boolean display; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + @Override + public Long getNetworkId() { + return networkId; + } + + @Override + public Long getId() { + return id; + } + + @Override + public FirewallRule.TrafficType getTrafficType() { + if (trafficType != null) { + return FirewallRule.TrafficType.valueOf(trafficType); + } + return null; + } + + @Override + public Long getIpAddressId() { + return null; + } + + @Override + public Boolean getDisplay() { + if (display != null) { + return display; + } + return super.getDisplay(); + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + Pair, Integer> result = routedIpv4Manager.listRoutingFirewallRules(this); + ListResponse response = new ListResponse<>(); + List ruleResponses = new ArrayList<>(); + + for (FirewallRule rule : result.first()) { + FirewallResponse ruleData = _responseGenerator.createFirewallResponse(rule); + ruleResponses.add(ruleData); + } + response.setResponses(ruleResponses, result.second()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } +} 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 new file mode 100644 index 000000000000..3c3a07ceece1 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/routing/UpdateRoutingFirewallRuleCmd.java @@ -0,0 +1,125 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.routing; + +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.BaseAsyncCustomIdCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.FirewallResponse; +import org.apache.cloudstack.api.response.FirewallRuleResponse; +import org.apache.cloudstack.context.CallContext; + +import com.cloud.event.EventTypes; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.rules.FirewallRule; +import com.cloud.user.Account; + +@APICommand(name = "updateRoutingFirewallRule", + description = "Updates Routing firewall rule with specified ID", + since = "4.20.0", + responseObject = FirewallRuleResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class UpdateRoutingFirewallRuleCmd extends BaseAsyncCustomIdCmd { + + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, required = true, description = "the ID of the Routing firewall rule") + private Long id; + + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the Routing firewall rule to the end user or not", + authorized = {RoleType.Admin}) + private Boolean display; + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + @Override + public boolean isDisplay() { + if (display != null) { + return display; + } else { + return true; + } + } + + public Long getId() { + return id; + } + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + FirewallRule rule = _firewallService.getFirewallRule(id); + if (rule != null) { + return rule.getAccountId(); + } + Account caller = CallContext.current().getCallingAccount(); + return caller.getAccountId(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_ROUTING_IPV4_FIREWALL_RULE_UPDATE; + } + + @Override + public String getEventDescription() { + return "Updating ipv4 routing firewall rule"; + } + + @Override + public void execute() throws ResourceUnavailableException { + CallContext.current().setEventDetails("Rule ID: " + getResourceUuid(ApiConstants.ID)); + FirewallRule rule = routedIpv4Manager.updateRoutingFirewallRule(this); + FirewallResponse ruleResponse = _responseGenerator.createFirewallResponse(rule); + setResponseObject(ruleResponse); + ruleResponse.setResponseName(getCommandName()); + } + + @Override + public void checkUuid() { + if (this.getCustomId() != null) { + _uuidMgr.checkUuid(this.getCustomId(), FirewallRule.class); + } + } + + @Override + public Long getApiResourceId() { + FirewallRule rule = _firewallService.getFirewallRule(id); + if (rule != null) { + return rule.getNetworkId(); + } + return null; + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.Network; + } +} 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 7545c3e09f40..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 @@ -16,17 +16,22 @@ // under the License. package org.apache.cloudstack.api.command.user.offering; -import org.apache.cloudstack.api.BaseListProjectAndAccountResourcesCmd; -import org.apache.cloudstack.api.response.StoragePoolResponse; -import org.apache.cloudstack.api.response.VolumeResponse; -import org.apache.cloudstack.api.response.ZoneResponse; +import static com.cloud.offering.DiskOffering.State.Active; 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.BaseCmd.CommandType; import org.apache.cloudstack.api.response.DiskOfferingResponse; import org.apache.cloudstack.api.response.ListResponse; +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; +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.StringUtils; + +import com.cloud.offering.DiskOffering.State; @APICommand(name = "listDiskOfferings", description = "Lists all available disk offerings.", responseObject = DiskOfferingResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -40,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; @@ -56,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, @@ -65,6 +70,18 @@ public class ListDiskOfferingsCmd extends BaseListProjectAndAccountResourcesCmd since = "4.19") private String storageType; + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, + description = "Filter by state of the disk offering. Defaults to 'Active'. If set to 'all' shows both Active & Inactive offerings.", + since = "4.19") + private String diskOfferingState; + + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, + type = CommandType.UUID, + entityType = UserVmResponse.class, + description = "The ID of a virtual machine. Pass this in if you want to see the suitable disk offering that can be used to create and add a disk to the virtual machine. Suitability is returned with suitableforvirtualmachine flag in the response", + since = "4.20.0") + private Long virtualMachineId; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -93,13 +110,27 @@ public String getStorageType() { return storageType; } + public State getState() { + if (StringUtils.isBlank(diskOfferingState)) { + return Active; + } + State state = EnumUtils.getEnumIgnoreCase(State.class, diskOfferingState); + if (!diskOfferingState.equalsIgnoreCase("all") && state == null) { + throw new IllegalArgumentException("Invalid state value: " + diskOfferingState); + } + return state; + } + + public Long getVirtualMachineId() { + return virtualMachineId; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @Override public void execute() { - ListResponse response = _queryService.searchForDiskOfferings(this); response.setResponseName(getCommandName()); this.setResponseObject(response); 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 e07d75a7d08f..5c5c8776bce3 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 @@ -16,15 +16,22 @@ // under the License. package org.apache.cloudstack.api.command.user.offering; -import org.apache.cloudstack.api.BaseListProjectAndAccountResourcesCmd; -import org.apache.cloudstack.api.response.ZoneResponse; +import static com.cloud.offering.ServiceOffering.State.Active; 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.ListResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; +import org.apache.cloudstack.api.response.TemplateResponse; import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.api.response.VgpuProfileResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.StringUtils; + +import com.cloud.offering.ServiceOffering.State; @APICommand(name = "listServiceOfferings", description = "Lists all available service offerings.", responseObject = ServiceOfferingResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -38,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; @@ -92,6 +99,31 @@ public class ListServiceOfferingsCmd extends BaseListProjectAndAccountResourcesC since = "4.19") private String storageType; + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, + description = "Filter by state of the service offering. Defaults to 'Active'. If set to 'all' shows both Active & Inactive offerings.", + since = "4.19") + private String serviceOfferingState; + + @Parameter(name = ApiConstants.TEMPLATE_ID, + type = CommandType.UUID, + entityType = TemplateResponse.class, + description = "The ID of the template that listed offerings must support", + since = "4.20.0") + private Long templateId; + + @Parameter(name = ApiConstants.VGPU_PROFILE_ID, + type = CommandType.UUID, + entityType = VgpuProfileResponse.class, + description = "The ID of the vGPU profile that listed offerings must support", + since = "4.21.0") + private Long vgpuProfileId; + + @Parameter(name = ApiConstants.GPU_ENABLED, + type = CommandType.BOOLEAN, + description = "Flag to indicate if the service offering supports GPU. If set to true, only service offerings that support GPU will be returned.", + since = "4.21.0") + private Boolean gpuEnabled; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -138,6 +170,29 @@ public String getStorageType() { return storageType; } + public State getState() { + if (StringUtils.isBlank(serviceOfferingState)) { + return Active; + } + State state = EnumUtils.getEnumIgnoreCase(State.class, serviceOfferingState); + if (!serviceOfferingState.equalsIgnoreCase("all") && state == null) { + throw new IllegalArgumentException("Invalid state value: " + serviceOfferingState); + } + return state; + } + + public Long getTemplateId() { + return templateId; + } + + public Long getVgpuProfileId() { + return vgpuProfileId; + } + + public Boolean getGpuEnabled() { + return gpuEnabled; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/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 d40d36634516..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 @@ -19,8 +19,6 @@ import java.util.ArrayList; import java.util.List; -import com.cloud.configuration.Resource; -import com.cloud.exception.InvalidParameterValueException; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseListProjectAndAccountResourcesCmd; @@ -28,7 +26,9 @@ import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.ResourceLimitResponse; +import com.cloud.configuration.Resource; import com.cloud.configuration.ResourceLimit; +import com.cloud.exception.InvalidParameterValueException; @APICommand(name = "listResourceLimits", description = "Lists resource limits.", responseObject = ResourceLimitResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -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. " @@ -72,6 +72,10 @@ public class ListResourceLimitsCmd extends BaseListProjectAndAccountResourcesCmd + "secondary_storage - SecondaryStorage. Total secondary storage space (in GiB) a user can use. ") private String resourceTypeName; + @Parameter(name = ApiConstants.TAG, type = CommandType.STRING, description = "Tag for the resource type", since = "4.20.0") + private String tag; + + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -88,6 +92,10 @@ public String getResourceTypeName() { return resourceTypeName; } + public String getTag() { + return tag; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -95,8 +103,8 @@ public String getResourceTypeName() { @Override public void execute() { List result = - _resourceLimitService.searchForLimits(id, _accountService.finalyzeAccountId(this.getAccountName(), this.getDomainId(), this.getProjectId(), false), this.getDomainId(), - getResourceTypeEnum(), this.getStartIndex(), this.getPageSizeVal()); + _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(); for (ResourceLimit limit : result) { 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 ae5188295397..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 @@ -34,8 +34,11 @@ import com.cloud.configuration.ResourceCount; import com.cloud.user.Account; -@APICommand(name = "updateResourceCount", description = "Recalculate and update resource count for an account or domain.", responseObject = ResourceCountResponse.class, - requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +@APICommand(name = "updateResourceCount", + description = "Recalculate and update resource count for an account or domain. " + + "This also executes some cleanup tasks before calculating resource counts.", + responseObject = ResourceCountResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class UpdateResourceCountCmd extends BaseCmd { @@ -58,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. " @@ -75,6 +78,9 @@ public class UpdateResourceCountCmd extends BaseCmd { @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "Update resource limits for project") private Long projectId; + @Parameter(name = ApiConstants.TAG, type = CommandType.STRING, description = "Tag for the resource type", since = "4.20.0") + private String tag; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -91,6 +97,10 @@ public Integer getResourceType() { return resourceType; } + public String getTag() { + return tag; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -117,7 +127,7 @@ public long getEntityOwnerId() { @Override public void execute() { List result = - _resourceLimitService.recalculateResourceCount(_accountService.finalyzeAccountId(accountName, domainId, projectId, true), getDomainId(), getResourceType()); + _resourceLimitService.recalculateResourceCount(_accountService.finalizeAccountId(accountName, domainId, projectId, true), getDomainId(), getResourceType(), getTag()); if ((result != null) && (result.size() > 0)) { ListResponse response = new ListResponse(); @@ -125,7 +135,6 @@ public void execute() { for (ResourceCount count : result) { ResourceCountResponse resourceCountResponse = _responseGenerator.createResourceCountResponse(count); - resourceCountResponse.setObjectName("resourcecount"); countResponses.add(resourceCountResponse); } 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 32b3b17527e2..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,12 +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. " - + "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. " @@ -70,6 +71,9 @@ public class UpdateResourceLimitCmd extends BaseCmd { + "11 - SecondaryStorage. Total secondary storage space (in GiB) a user can use. ") private Integer resourceType; + @Parameter(name = ApiConstants.TAG, type = CommandType.STRING, description = "Tag for the resource type", since = "4.20.0") + private String tag; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -82,6 +86,10 @@ public Long getDomainId() { return domainId; } + public String getTag() { + return tag; + } + public Integer getResourceType() { return resourceType; } @@ -92,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(); } @@ -102,7 +110,7 @@ public long getEntityOwnerId() { @Override public void execute() { - ResourceLimit result = _resourceLimitService.updateResourceLimit(_accountService.finalyzeAccountId(accountName, domainId, projectId, true), getDomainId(), resourceType, max); + 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/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 07973fcbfca5..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 @@ -17,9 +17,13 @@ package org.apache.cloudstack.api.command.user.snapshot; -import java.util.ArrayList; -import java.util.List; - +import com.cloud.dc.DataCenter; +import com.cloud.event.EventTypes; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.exception.StorageUnavailableException; +import com.cloud.storage.Snapshot; +import com.cloud.user.Account; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; @@ -31,26 +35,24 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.command.user.UserCmd; import org.apache.cloudstack.api.response.SnapshotResponse; +import org.apache.cloudstack.api.response.StoragePoolResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; import org.apache.commons.collections.CollectionUtils; - -import com.cloud.dc.DataCenter; -import com.cloud.event.EventTypes; -import com.cloud.exception.ResourceAllocationException; -import com.cloud.exception.ResourceUnavailableException; -import com.cloud.exception.StorageUnavailableException; -import com.cloud.storage.Snapshot; -import com.cloud.user.Account; +import org.apache.commons.lang3.BooleanUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import java.util.ArrayList; +import java.util.List; + @APICommand(name = "copySnapshot", description = "Copies a snapshot from one zone to another.", responseObject = SnapshotResponse.class, responseView = ResponseObject.ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.19.0", authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) public class CopySnapshotCmd extends BaseAsyncCmd implements UserCmd { public static final Logger logger = LogManager.getLogger(CopySnapshotCmd.class.getName()); + private Snapshot snapshot; ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// @@ -84,6 +86,24 @@ public class CopySnapshotCmd extends BaseAsyncCmd implements UserCmd { "Do not specify destzoneid and destzoneids together, however one of them is required.") protected List destZoneIds; + @Parameter(name = ApiConstants.STORAGE_ID_LIST, + type=CommandType.LIST, + collectionType = CommandType.UUID, + entityType = StoragePoolResponse.class, + required = false, + authorized = RoleType.Admin, + since = "4.21.0", + description = "A comma-separated list of IDs of the storage pools in other zones in which the snapshot will be made available. " + + "The snapshot will always be made available in the zone in which the volume is present. Currently supported for StorPool only") + protected List storagePoolIds; + + @Parameter (name = ApiConstants.USE_STORAGE_REPLICATION, + type=CommandType.BOOLEAN, + 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 /////////////////////// ///////////////////////////////////////////////////// @@ -106,7 +126,15 @@ public List getDestinationZoneIds() { destIds.add(destZoneId); return destIds; } - return null; + return new ArrayList<>(); + } + + public List getStoragePoolIds() { + return storagePoolIds; + } + + public Boolean useStorageReplication() { + return BooleanUtils.toBoolean(useStorageReplication); } @Override @@ -116,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 @@ -152,7 +184,7 @@ public long getEntityOwnerId() { @Override public void execute() throws ResourceUnavailableException { try { - if (destZoneId == null && CollectionUtils.isEmpty(destZoneIds)) + if (destZoneId == null && CollectionUtils.isEmpty(destZoneIds) && useStorageReplication()) throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Either destzoneid or destzoneids parameters have to be specified."); @@ -161,7 +193,7 @@ public void execute() throws ResourceUnavailableException { "Both destzoneid and destzoneids cannot be specified at the same time."); CallContext.current().setEventDetails(getEventDescription()); - Snapshot snapshot = _snapshotService.copySnapshot(this); + snapshot = _snapshotService.copySnapshot(this); if (snapshot != null) { SnapshotResponse response = _queryService.listSnapshot(this); @@ -177,6 +209,13 @@ public void execute() throws ResourceUnavailableException { logger.warn("Exception: ", ex); throw new ServerApiException(ApiErrorCode.RESOURCE_ALLOCATION_ERROR, ex.getMessage()); } + } + + public Snapshot getSnapshot() { + return snapshot; + } + public void setSnapshot(Snapshot snapshot) { + this.snapshot = snapshot; } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java index 3289ac2fe106..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 @@ -16,11 +16,13 @@ // under the License. package org.apache.cloudstack.api.command.user.snapshot; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -32,6 +34,7 @@ import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.SnapshotPolicyResponse; import org.apache.cloudstack.api.response.SnapshotResponse; +import org.apache.cloudstack.api.response.StoragePoolResponse; import org.apache.cloudstack.api.response.VolumeResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.commons.collections.MapUtils; @@ -46,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 { @@ -56,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") @@ -71,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)") @@ -99,6 +102,22 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd { since = "4.19.0") protected List zoneIds; + @Parameter(name = ApiConstants.STORAGE_ID_LIST, + type=CommandType.LIST, + collectionType = CommandType.UUID, + entityType = StoragePoolResponse.class, + authorized = RoleType.Admin, + description = "A comma-separated list of IDs of the storage pools in other zones in which the snapshot will be made available. " + + "The snapshot will always be made available in the zone in which the volume is present.", + since = "4.21.0") + protected List storagePoolIds; + + @Parameter (name = ApiConstants.USE_STORAGE_REPLICATION, + type=CommandType.BOOLEAN, + 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; // /////////////////////////////////////////////////// @@ -161,6 +180,17 @@ public List getZoneIds() { return zoneIds; } + public List getStoragePoolIds() { + return storagePoolIds == null ? new ArrayList<>() : storagePoolIds; + } + + public Boolean useStorageReplication() { + if (useStorageReplication == null) { + return false; + } + return useStorageReplication; + } + // /////////////////////////////////////////////////// // ///////////// API Implementation/////////////////// // /////////////////////////////////////////////////// @@ -186,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(); @@ -199,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 @@ -209,12 +239,12 @@ public ApiCommandResourceType getApiResourceType() { @Override public void create() throws ResourceAllocationException { - Snapshot snapshot = _volumeService.allocSnapshot(getVolumeId(), getPolicyId(), getSnapshotName(), getLocationType(), getZoneIds()); + Snapshot snapshot = _volumeService.allocSnapshot(getVolumeId(), getPolicyId(), getSnapshotName(), getLocationType(), getZoneIds(), getStoragePoolIds(), useStorageReplication()); if (snapshot != null) { setEntityId(snapshot.getId()); setEntityUuid(snapshot.getUuid()); } 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)); } } @@ -223,29 +253,28 @@ public void execute() { Snapshot snapshot; try { snapshot = - _volumeService.takeSnapshot(getVolumeId(), getPolicyId(), getEntityId(), _accountService.getAccount(getEntityOwnerId()), getQuiescevm(), getLocationType(), getAsyncBackup(), getTags(), getZoneIds()); + _volumeService.takeSnapshot(getVolumeId(), getPolicyId(), getEntityId(), _accountService.getAccount(getEntityOwnerId()), getQuiescevm(), getLocationType(), getAsyncBackup(), getTags(), getZoneIds(), getStoragePoolIds(), useStorageReplication()); if (snapshot != null) { SnapshotResponse response = _responseGenerator.createSnapshotResponse(snapshot); 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); } } - private Snapshot.LocationType getLocationType() { - - if (Snapshot.LocationType.values() == null || Snapshot.LocationType.values().length == 0 || locationType == null) { + public Snapshot.LocationType getLocationType() { + if (locationType == null) { return null; } @@ -283,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 6bebdc09f59b..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("Unable to find project by account id=" + account.getUuid()); + throw new InvalidParameterValueException(String.format("Unable to find project by Account %s", account)); } if (project.getState() != Project.State.Active) { - throw new PermissionDeniedException("Can't add resources to the project id=" + project.getUuid() + " in state=" + project.getState() + " as it's no longer 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 @@ -153,19 +153,20 @@ public ApiCommandResourceType getApiResourceType() { @Override public void create() throws ResourceAllocationException { - Snapshot snapshot = this._volumeService.allocSnapshotForVm(getVmId(), getVolumeId(), getSnapshotName()); + Snapshot snapshot = this._volumeService.allocSnapshotForVm(getVmId(), getVolumeId(), getSnapshotName(), getVMSnapshotId()); if (snapshot != null) { this.setEntityId(snapshot.getId()); this.setEntityUuid(snapshot.getUuid()); } 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() { - logger.info("CreateSnapshotFromVMSnapshotCmd with vm snapshot id:" + getVMSnapshotId() + " and snapshot id:" + getEntityId() + " starts:" + System.currentTimeMillis()); - CallContext.current().setEventDetails("Vm Snapshot Id: "+ this._uuidMgr.getUuid(VMSnapshot.class, getVMSnapshotId())); + VMSnapshot vmSnapshot = _vmSnapshotService.getVMSnapshotById(getVMSnapshotId()); + 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()); @@ -174,19 +175,19 @@ public void execute() { response.setResponseName(getCommandName()); this.setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create snapshot due to an internal error creating snapshot from vm snapshot " + getVMSnapshotId()); + 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, "Failed to create snapshot due to an internal error creating snapshot from vm snapshot " + getVMSnapshotId()); + 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" + getEntityId()); + logger.debug("Failed to clean failed Snapshot {} with ID {}", () -> _entityMgr.findById(Snapshot.class, getEntityId()), this::getEntityId); } } } @@ -208,4 +209,9 @@ public Long getSyncObjId() { } return null; } + + @Override + public Long getApiResourceId() { + return getEntityId(); + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotPolicyCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotPolicyCmd.java index e30b897db2ea..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 @@ -16,11 +16,13 @@ // under the License. package org.apache.cloudstack.api.command.user.snapshot; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.PermissionDeniedException; +import com.cloud.projects.Project; +import com.cloud.storage.Volume; +import com.cloud.storage.snapshot.SnapshotPolicy; +import com.cloud.user.Account; +import java.util.ArrayList; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; @@ -30,18 +32,18 @@ import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.SnapshotPolicyResponse; +import org.apache.cloudstack.api.response.StoragePoolResponse; import org.apache.cloudstack.api.response.VolumeResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.commons.collections.MapUtils; -import com.cloud.exception.InvalidParameterValueException; -import com.cloud.exception.PermissionDeniedException; -import com.cloud.projects.Project; -import com.cloud.storage.Volume; -import com.cloud.storage.snapshot.SnapshotPolicy; -import com.cloud.user.Account; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.commons.lang3.BooleanUtils; -@APICommand(name = "createSnapshotPolicy", description = "Creates a snapshot policy for the account.", responseObject = SnapshotPolicyResponse.class, +@APICommand(name = "createSnapshotPolicy", description = "Creates a Snapshot policy for the account.", responseObject = SnapshotPolicyResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateSnapshotPolicyCmd extends BaseCmd { @@ -50,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; @@ -66,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)") @@ -83,6 +85,21 @@ public class CreateSnapshotPolicyCmd extends BaseCmd { "The snapshots will always be made available in the zone in which the volume is present.") protected List zoneIds; + @Parameter(name = ApiConstants.STORAGE_ID_LIST, + type=CommandType.LIST, + collectionType = CommandType.UUID, + entityType = StoragePoolResponse.class, + description = "A comma-separated list of IDs of the storage pools in other zones in which the snapshot will be made available. " + + "The snapshot will always be made available in the zone in which the volume is present.", + since = "4.21.0") + protected List storagePoolIds; + + @Parameter (name = ApiConstants.USE_STORAGE_REPLICATION, + type=CommandType.BOOLEAN, + 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 /////////////////////// ///////////////////////////////////////////////////// @@ -119,6 +136,14 @@ public List getZoneIds() { return zoneIds; } + public List getStoragePoolIds() { + return storagePoolIds == null ? new ArrayList<>() : storagePoolIds; + } + + public Boolean useStorageReplication() { + return BooleanUtils.toBoolean(useStorageReplication); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -141,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(); @@ -167,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 new file mode 100644 index 000000000000..dacdd20b3969 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/ExtractSnapshotCmd.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.snapshot; + +import com.cloud.event.EventTypes; +import com.cloud.storage.Snapshot; +import com.cloud.user.Account; +import org.apache.cloudstack.acl.SecurityChecker.AccessType; +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.ExtractResponse; +import org.apache.cloudstack.api.response.SnapshotResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.context.CallContext; + +@APICommand(name = "extractSnapshot", description = "Returns a download URL for extracting a snapshot. It must be in the Backed Up state.", since = "4.20.0", + responseObject = ExtractResponse.class, entityType = {Snapshot.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class ExtractSnapshotCmd extends BaseAsyncCmd { + + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @ACL(accessType = AccessType.OperateEntry) + @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType=SnapshotResponse.class, required=true, since="4.20.0", description="the ID of the snapshot") + private Long id; + + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, since="4.20.0", + description = "the ID of the zone where the snapshot is located") + private Long zoneId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public Long getZoneId() { + return zoneId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.Snapshot; + } + + @Override + public Long getApiResourceId() { + return getId(); + } + + /** + * @return ID of the snapshot to extract, if any. Otherwise returns the ACCOUNT_ID_SYSTEM, so ERROR events will be traceable. + */ + @Override + public long getEntityOwnerId() { + Snapshot snapshot = _entityMgr.findById(Snapshot.class, getId()); + if (snapshot != null) { + return snapshot.getAccountId(); + } + + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_SNAPSHOT_EXTRACT; + } + + @Override + public String getEventDescription() { + return "Starting Snapshot extraction for Snapshot with ID: " + getResourceUuid(ApiConstants.ID); + } + + @Override + public void execute() { + CallContext.current().setEventDetails("Snapshot ID: " + getResourceUuid(ApiConstants.ID)); + String uploadUrl = _snapshotService.extractSnapshot(this); + logger.info("Extract URL [{}] of snapshot [{}].", uploadUrl, id); + if (uploadUrl != null) { + ExtractResponse response = _responseGenerator.createSnapshotExtractResponse(id, zoneId, getEntityOwnerId(), uploadUrl); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to extract snapshot"); + } + } +} 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 new file mode 100644 index 000000000000..24290bc345e1 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/ChangeSharedFSDiskOfferingCmd.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.storage.sharedfs; + +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.ResponseObject; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.DiskOfferingResponse; +import org.apache.cloudstack.api.response.SharedFSResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.storage.sharedfs.SharedFS; +import org.apache.cloudstack.storage.sharedfs.SharedFSService; + +import com.cloud.event.EventTypes; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.user.Account; + +@APICommand(name = "changeSharedFileSystemDiskOffering", + responseObject= SharedFSResponse.class, + description = "Change Disk offering of a Shared FileSystem", + responseView = ResponseObject.ResponseView.Restricted, + entityType = SharedFS.class, + requestHasSensitiveInfo = false, + since = "4.20.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class ChangeSharedFSDiskOfferingCmd extends BaseAsyncCmd implements UserCmd { + + @Inject + SharedFSService sharedFSService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, + type = CommandType.UUID, + required = true, + entityType = SharedFSResponse.class, + description = "the ID of the shared filesystem") + private Long id; + + @Parameter(name = ApiConstants.DISK_OFFERING_ID, + type = CommandType.UUID, + entityType = DiskOfferingResponse.class, + description = "the disk offering to use for the underlying storage") + private Long diskOfferingId; + + @Parameter(name = ApiConstants.SIZE, + type = CommandType.LONG, + description = "the size of the shared filesystem in GiB") + private Long size; + + @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") + private Long maxIops; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public Long getSize() { + return size; + } + + public Long getDiskOfferingId() { + return diskOfferingId; + } + + public Long getMinIops() { + return minIops; + } + + public Long getMaxIops() { + return maxIops; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getEventType() { + return EventTypes.EVENT_SHAREDFS_CHANGE_DISK_OFFERING; + } + + @Override + public String getEventDescription() { + return "Changing disk offering for the Shared FileSystem with ID:" + getResourceUuid(ApiConstants.ID); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } + + @Override + public void execute() throws ResourceAllocationException { + SharedFS sharedFS = sharedFSService.changeSharedFSDiskOffering(this); + if (sharedFS != null) { + ResponseObject.ResponseView respView = getResponseView(); + Account caller = CallContext.current().getCallingAccount(); + if (_accountService.isRootAdmin(caller.getId())) { + respView = ResponseObject.ResponseView.Full; + } + SharedFSResponse response = _responseGenerator.createSharedFSResponse(respView, sharedFS); + response.setObjectName(SharedFS.class.getSimpleName().toLowerCase()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to change disk offering for the Shared FileSystem"); + } + } +} 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 new file mode 100644 index 000000000000..1ac0f27067b4 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/ChangeSharedFSServiceOfferingCmd.java @@ -0,0 +1,147 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.storage.sharedfs; + +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.ResponseObject; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.SharedFSResponse; +import org.apache.cloudstack.api.response.ServiceOfferingResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.storage.sharedfs.SharedFS; +import org.apache.cloudstack.storage.sharedfs.SharedFSService; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ManagementServerException; +import com.cloud.exception.OperationTimedoutException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.exception.VirtualMachineMigrationException; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "changeSharedFileSystemServiceOffering", + responseObject= SharedFSResponse.class, + description = "Change Service offering of a Shared FileSystem", + responseView = ResponseObject.ResponseView.Restricted, + entityType = SharedFS.class, + requestHasSensitiveInfo = false, + since = "4.20.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class ChangeSharedFSServiceOfferingCmd extends BaseAsyncCmd implements UserCmd { + + @Inject + SharedFSService sharedFSService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, + type = CommandType.UUID, + required = true, + entityType = SharedFSResponse.class, + description = "the ID of the shared filesystem") + private Long id; + + @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, + type = CommandType.UUID, + entityType = ServiceOfferingResponse.class, + required = true, + description = "the offering to use for the shared filesystem instance") + private Long serviceOfferingId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public Long getServiceOfferingId() { + return serviceOfferingId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getEventType() { + return EventTypes.EVENT_SHAREDFS_CHANGE_SERVICE_OFFERING; + } + + @Override + public String getEventDescription() { + return "Changing service offering for the Shared FileSystem with ID:" + getResourceUuid(ApiConstants.ID); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } + + private String getExceptionMsg(Exception ex) { + return "Shared FileSystem restart failed with exception" + ex.getMessage(); + } + + @Override + public void execute() { + SharedFS sharedFS; + try { + sharedFS = sharedFSService.changeSharedFSServiceOffering(this); + } catch (ResourceUnavailableException ex) { + logger.warn("Shared FileSystem change service offering exception: ", ex); + throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, getExceptionMsg(ex)); + } catch (InsufficientCapacityException ex) { + logger.warn("Shared FileSystem change service offering exception: ", ex); + throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, getExceptionMsg(ex)); + } catch (OperationTimedoutException ex) { + logger.warn("Shared FileSystem change service offering exception: ", ex); + throw new CloudRuntimeException("Shared FileSystem change service offering timed out due to " + ex.getMessage()); + } catch (ManagementServerException ex) { + logger.warn("Shared FileSystem change service offering exception: ", ex); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } catch (VirtualMachineMigrationException ex) { + logger.warn("Shared FileSystem change service offering exception: ", ex); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + + if (sharedFS != null) { + ResponseObject.ResponseView respView = getResponseView(); + Account caller = CallContext.current().getCallingAccount(); + if (_accountService.isRootAdmin(caller.getId())) { + respView = ResponseObject.ResponseView.Full; + } + SharedFSResponse response = _responseGenerator.createSharedFSResponse(respView, sharedFS); + response.setObjectName(SharedFS.class.getSimpleName().toLowerCase()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to change the service offering for the Shared FileSystem"); + } + } +} 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 new file mode 100644 index 000000000000..595b611b5c0d --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/CreateSharedFSCmd.java @@ -0,0 +1,304 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.storage.sharedfs; + +import javax.inject.Inject; + +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.OperationTimedoutException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +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.BaseAsyncCreateCmd; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.DiskOfferingResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.SharedFSResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.api.response.ServiceOfferingResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.storage.sharedfs.SharedFS; +import org.apache.cloudstack.storage.sharedfs.SharedFSProvider; +import org.apache.cloudstack.storage.sharedfs.SharedFSService; + +@APICommand(name = "createSharedFileSystem", + responseObject= SharedFSResponse.class, + description = "Create a new Shared File System of specified size and disk offering, attached to the given network", + responseView = ResponseObject.ResponseView.Restricted, + entityType = SharedFS.class, + requestHasSensitiveInfo = false, + since = "4.20.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class CreateSharedFSCmd extends BaseAsyncCreateCmd implements UserCmd { + + @Inject + SharedFSService sharedFSService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NAME, + type = CommandType.STRING, + required = true, + description = "the name of the shared filesystem.") + private String name; + + @Parameter(name = ApiConstants.ACCOUNT, + type = BaseCmd.CommandType.STRING, + description = "the account associated with the shared filesystem. 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 shared filesystem. If used with the account parameter" + + " returns the shared filesystem associated with the account for the specified domain." + + "If account is NOT provided then the shared filesystem will be assigned to the caller account and domain.") + private Long domainId; + + @Parameter(name = ApiConstants.PROJECT_ID, + type = CommandType.UUID, + entityType = ProjectResponse.class, + description = "the project associated with the shared filesystem. Mutually exclusive with account parameter") + private Long projectId; + + @Parameter(name = ApiConstants.DESCRIPTION, + type = CommandType.STRING, + description = "the description for the shared filesystem.") + private String description; + + @Parameter(name = ApiConstants.SIZE, + type = CommandType.LONG, + description = "the size of the shared filesystem in GiB") + private Long size; + + @Parameter(name = ApiConstants.ZONE_ID, + type = CommandType.UUID, + required = true, + entityType = ZoneResponse.class, + description = "the zone id.") + private Long zoneId; + + @Parameter(name = ApiConstants.DISK_OFFERING_ID, + type = CommandType.UUID, + required = true, + entityType = DiskOfferingResponse.class, + description = "the disk offering to use for the underlying storage. This will define the size and other specifications like encryption and qos for the shared filesystem.") + private Long diskOfferingId; + + @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") + private Long maxIops; + + @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, + type = CommandType.UUID, + required = true, + entityType = ServiceOfferingResponse.class, + description = "the service offering to use for the shared filesystem instance hosting the data. The offering should be HA enabled and the cpu count and memory size should be greater than equal to sharedfsvm.min.cpu.count and sharedfsvm.min.ram.size respectively") + private Long serviceOfferingId; + + @Parameter(name = ApiConstants.FILESYSTEM, + type = CommandType.STRING, + required = true, + description = "the filesystem format (XFS / EXT4) which will be installed on the shared filesystem.") + private String fsFormat; + + @Parameter(name = ApiConstants.PROVIDER, + type = CommandType.STRING, + description = "the provider to be used for the shared filesystem. The list of providers can be fetched by using the listSharedFileSystemProviders API.") + private String sharedFSProviderName; + + @Parameter(name = ApiConstants.NETWORK_ID, + type = CommandType.UUID, + required = true, + entityType = NetworkResponse.class, + description = "network to attach the shared filesystem to") + private Long networkId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getName() { + return name; + } + + + public Long getProjectId() { + return projectId; + } + + public Long getDomainId() { + return domainId; + } + + public String getAccountName() { + return accountName; + } + public String getDescription() { + return description; + } + + public Long getSize() { + return size; + } + + public Long getZoneId() { + return zoneId; + } + + public Long getDiskOfferingId() { + return diskOfferingId; + } + + public Long getServiceOfferingId() { + return serviceOfferingId; + } + + public Long getMaxIops() { + return maxIops; + } + + public Long getMinIops() { + return minIops; + } + + public String getFsFormat() { + return fsFormat; + } + + public Long getNetworkId() { + return networkId; + } + + public String getSharedFSProviderName() { + if (sharedFSProviderName != null) { + return sharedFSProviderName; + } else { + return SharedFSProvider.SharedFSProviderType.SHAREDFSVM.toString(); + } + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.SharedFS; + } + + @Override + public Long getApiResourceId() { + return this.getEntityId(); + } + @Override + public long getEntityOwnerId() { + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); + if (accountId == null) { + return CallContext.current().getCallingAccount().getId(); + } + return accountId; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_SHAREDFS_CREATE; + } + + @Override + public String getEventDescription() { + return "Creating shared filesystem " + name; + } + + private String getCreateExceptionMsg(Exception ex) { + return "Shared FileSystem create failed with exception" + ex.getMessage(); + } + + private String getStartExceptionMsg(Exception ex) { + return "Shared FileSystem start failed with exception: " + ex.getMessage(); + } + + public void create() { + SharedFS sharedFS; + sharedFS = sharedFSService.allocSharedFS(this); + if (sharedFS != null) { + setEntityId(sharedFS.getId()); + setEntityUuid(sharedFS.getUuid()); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Shared FileSystem"); + } + } + + @Override + public void execute() { + SharedFS sharedFS; + try { + sharedFS = sharedFSService.deploySharedFS(this); + } catch (ResourceUnavailableException ex) { + logger.warn("Shared FileSystem start exception: ", ex); + throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, getStartExceptionMsg(ex)); + } catch (ConcurrentOperationException ex) { + logger.warn("Shared FileSystem start exception: ", ex); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, getStartExceptionMsg(ex)); + } catch (InsufficientCapacityException ex) { + logger.warn("Shared FileSystem start exception: ", ex); + throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, getStartExceptionMsg(ex)); + } catch (ResourceAllocationException ex) { + logger.warn("Shared FileSystem start exception: ", ex); + throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage()); + } catch (OperationTimedoutException ex) { + throw new CloudRuntimeException("Shared FileSystem start timed out due to " + ex.getMessage()); + } + + if (sharedFS != null) { + ResponseObject.ResponseView respView = getResponseView(); + Account caller = CallContext.current().getCallingAccount(); + if (_accountService.isRootAdmin(caller.getId())) { + respView = ResponseObject.ResponseView.Full; + } + SharedFSResponse response = _responseGenerator.createSharedFSResponse(respView, sharedFS); + response.setObjectName(SharedFS.class.getSimpleName().toLowerCase()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to start Shared FileSystem"); + } + } +} 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 new file mode 100644 index 000000000000..35f16a4dc2a0 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/DestroySharedFSCmd.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.storage.sharedfs; + +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.ResponseObject; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.SharedFSResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.storage.sharedfs.SharedFS; +import org.apache.cloudstack.storage.sharedfs.SharedFSService; + +import javax.inject.Inject; + +import com.cloud.event.EventTypes; + +@APICommand(name = "destroySharedFileSystem", + responseObject= SuccessResponse.class, + description = "Destroy a Shared FileSystem by id", + responseView = ResponseObject.ResponseView.Restricted, + entityType = SharedFS.class, + requestHasSensitiveInfo = false, + since = "4.20.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class DestroySharedFSCmd extends BaseAsyncCmd implements UserCmd { + + @Inject + SharedFSService sharedFSService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, + type = CommandType.UUID, + entityType = SharedFSResponse.class, + description = "the ID of the shared filesystem to delete") + private Long id; + + @Parameter(name = ApiConstants.EXPUNGE, + type = CommandType.BOOLEAN, + description = "If true is passed, the shared filesystem is expunged immediately. False by default.") + private Boolean expunge; + + @Parameter(name = ApiConstants.FORCED, + type = CommandType.BOOLEAN, + description = "If true is passed, the shared filesystem can be destroyed without stopping it first.") + private Boolean forced; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public boolean isExpunge() { + return (expunge != null) ? expunge : false; + } + + public boolean isForced() { + return (forced != null) ? forced : false; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getEventType() { + return EventTypes.EVENT_SHAREDFS_DESTROY; + } + + @Override + public String getEventDescription() { + return "Destroying Shared FileSystem with ID:" + getResourceUuid(ApiConstants.ID); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } + + @Override + public void execute() { + Boolean result = sharedFSService.destroySharedFS(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to destroy Shared FileSystem"); + } + } +} 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 new file mode 100644 index 000000000000..8960aa3e4d40 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/ExpungeSharedFSCmd.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.storage.sharedfs; + +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.ResponseObject; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.SharedFSResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.storage.sharedfs.SharedFS; +import org.apache.cloudstack.storage.sharedfs.SharedFSService; + +import com.cloud.event.EventTypes; + +@APICommand(name = "expungeSharedFileSystem", + responseObject= SuccessResponse.class, + description = "Expunge a Shared FileSystem by id", + responseView = ResponseObject.ResponseView.Restricted, + entityType = SharedFS.class, + requestHasSensitiveInfo = false, + since = "4.20.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class ExpungeSharedFSCmd extends BaseAsyncCmd implements UserCmd { + + @Inject + SharedFSService sharedFSService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = SharedFSResponse.class, description = "the ID of the shared filesystem to expunge") + private Long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getEventType() { + return EventTypes.EVENT_SHAREDFS_EXPUNGE; + } + + @Override + public String getEventDescription() { + return "Expunging Shared FileSystem with ID: " + getResourceUuid(ApiConstants.ID); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } + + @Override + public void execute() { + try { + sharedFSService.deleteSharedFS(id); + } catch (Exception ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to expunge Shared FileSystem"); + } finally { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/ListSharedFSCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/ListSharedFSCmd.java new file mode 100644 index 000000000000..c52c691ac0b9 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/ListSharedFSCmd.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.user.storage.sharedfs; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListRetrieveOnlyResourceCountCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.DiskOfferingResponse; +import org.apache.cloudstack.api.response.SharedFSResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.api.response.ServiceOfferingResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.storage.sharedfs.SharedFS; +import org.apache.cloudstack.storage.sharedfs.SharedFSService; + +import javax.inject.Inject; + +@APICommand(name = "listSharedFileSystems", + responseObject= SharedFSResponse.class, + description = "List Shared FileSystems", + responseView = ResponseObject.ResponseView.Restricted, + entityType = SharedFS.class, + requestHasSensitiveInfo = false, + since = "4.20.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class ListSharedFSCmd extends BaseListRetrieveOnlyResourceCountCmd implements UserCmd { + + @Inject + SharedFSService sharedFSService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = SharedFSResponse.class, description = "the ID of the shared filesystem") + private Long id; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the shared filesystem") + private String name; + + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the ID of the availability zone") + private Long zoneId; + + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "the ID of the network") + private Long networkId; + + @Parameter(name = ApiConstants.DISK_OFFERING_ID, type = CommandType.UUID, entityType = DiskOfferingResponse.class, description = "the disk offering of the shared filesystem") + private Long diskOfferingId; + + @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, description = "the service offering of the shared filesystem") + private Long serviceOfferingId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public Long getZoneId() { + return zoneId; + } + + public Long getNetworkId() { + return networkId; + } + + public Long getDiskOfferingId() { + return diskOfferingId; + } + + public Long getServiceOfferingId() { + return serviceOfferingId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + public long getEntityOwnerId() { + return 0; + } + + @Override + public void execute() { + ListResponse response = sharedFSService.searchForSharedFS(getResponseView(), this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/ListSharedFSProvidersCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/ListSharedFSProvidersCmd.java new file mode 100644 index 000000000000..940e07225cf9 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/ListSharedFSProvidersCmd.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.storage.sharedfs; + +import java.util.ArrayList; +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.BaseListCmd; +import org.apache.cloudstack.api.response.SharedFSProviderResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.storage.sharedfs.SharedFSProvider; +import org.apache.cloudstack.storage.sharedfs.SharedFSService; + +@APICommand(name = "listSharedFileSystemProviders", + responseObject = SharedFSProviderResponse.class, + description = "Lists all available shared filesystem providers.", + requestHasSensitiveInfo = false, + since = "4.20.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class ListSharedFSProvidersCmd extends BaseListCmd { + + @Inject + public SharedFSService sharedFSService; + + @Override + public void execute() { + List sharedFSProviders = sharedFSService.getSharedFSProviders(); + final ListResponse response = new ListResponse<>(); + final List responses = new ArrayList<>(); + + for (SharedFSProvider sharedFSProvider : sharedFSProviders) { + SharedFSProviderResponse sharedFSProviderResponse = new SharedFSProviderResponse(); + sharedFSProviderResponse.setName(sharedFSProvider.getName()); + sharedFSProviderResponse.setObjectName("sharedfilesystemprovider"); + responses.add(sharedFSProviderResponse); + } + response.setResponses(responses, responses.size()); + response.setResponseName(this.getCommandName()); + setResponseObject(response); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/RecoverSharedFSCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/RecoverSharedFSCmd.java new file mode 100644 index 000000000000..6e5bbaa4d8a8 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/RecoverSharedFSCmd.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.storage.sharedfs; + +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.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.SharedFSResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.storage.sharedfs.SharedFS; +import org.apache.cloudstack.storage.sharedfs.SharedFSService; + +@APICommand(name = "recoverSharedFileSystem", + responseObject= SuccessResponse.class, + description = "Recover a Shared FileSystem by id", + responseView = ResponseObject.ResponseView.Restricted, + entityType = SharedFS.class, + requestHasSensitiveInfo = false, + since = "4.20.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class RecoverSharedFSCmd extends BaseCmd implements UserCmd { + + @Inject + SharedFSService sharedFSService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = SharedFSResponse.class, description = "the ID of the shared filesystem to recover") + private Long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } + + @Override + public void execute() { + SharedFS sharedFS = sharedFSService.recoverSharedFS(id); + if (sharedFS != null) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to recover Shared FileSystem"); + } + } +} 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 new file mode 100644 index 000000000000..75565796caa4 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/RestartSharedFSCmd.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.storage.sharedfs; + +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.ResponseObject; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.SharedFSResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.storage.sharedfs.SharedFS; +import org.apache.cloudstack.storage.sharedfs.SharedFSService; + +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.OperationTimedoutException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "restartSharedFileSystem", + responseObject= SuccessResponse.class, + description = "Restart a Shared FileSystem", + responseView = ResponseObject.ResponseView.Restricted, + entityType = SharedFS.class, + requestHasSensitiveInfo = false, + since = "4.20.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class RestartSharedFSCmd extends BaseAsyncCmd implements UserCmd { + + @Inject + SharedFSService sharedFSService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, + type = CommandType.UUID, + required = true, + entityType = SharedFSResponse.class, + description = "the ID of the shared filesystem") + private Long id; + + @Parameter(name = ApiConstants.CLEANUP, + type = CommandType.BOOLEAN, + description = "is cleanup required") + private boolean cleanup; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public Boolean getCleanup() { + return cleanup; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getEventType() { + return EventTypes.EVENT_SHAREDFS_RESTART; + } + + @Override + public String getEventDescription() { + return "Restarting Shared FileSystem with ID: " + getResourceUuid(ApiConstants.ID); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } + + private String getRestartExceptionMsg(Exception ex) { + return "Shared FileSystem restart failed with exception" + ex.getMessage(); + } + + @Override + public void execute() { + SharedFS sharedFS; + try { + sharedFS = sharedFSService.restartSharedFS(this.getId(), this.getCleanup()); + } catch (ResourceUnavailableException ex) { + logger.warn("Shared FileSystem restart exception: ", ex); + throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, getRestartExceptionMsg(ex)); + } catch (ConcurrentOperationException ex) { + logger.warn("Shared FileSystem restart exception: ", ex); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, getRestartExceptionMsg(ex)); + } catch (InsufficientCapacityException ex) { + logger.warn("Shared FileSystem restart exception: ", ex); + throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, getRestartExceptionMsg(ex)); + } catch (ResourceAllocationException ex) { + logger.warn("Shared FileSystem restart exception: ", ex); + throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage()); + } catch (OperationTimedoutException ex) { + logger.warn("Shared FileSystem restart exception: ", ex); + throw new CloudRuntimeException("Shared FileSystem start timed out due to " + ex.getMessage()); + } + + if (sharedFS != null) { + ResponseObject.ResponseView respView = getResponseView(); + Account caller = CallContext.current().getCallingAccount(); + if (_accountService.isRootAdmin(caller.getId())) { + respView = ResponseObject.ResponseView.Full; + } + SharedFSResponse response = _responseGenerator.createSharedFSResponse(respView, sharedFS); + response.setObjectName(SharedFS.class.getSimpleName().toLowerCase()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to restart Shared FileSystem"); + } + } +} 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 new file mode 100644 index 000000000000..d7440b532b31 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/StartSharedFSCmd.java @@ -0,0 +1,135 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.storage.sharedfs; + +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.ResponseObject; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.SharedFSResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.storage.sharedfs.SharedFS; +import org.apache.cloudstack.storage.sharedfs.SharedFSService; + +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.OperationTimedoutException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "startSharedFileSystem", + responseObject= SharedFSResponse.class, + description = "Start a Shared FileSystem", + responseView = ResponseObject.ResponseView.Restricted, + entityType = SharedFS.class, + requestHasSensitiveInfo = false, + since = "4.20.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class StartSharedFSCmd extends BaseAsyncCmd implements UserCmd { + + @Inject + SharedFSService sharedFSService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, + type = CommandType.UUID, + required = true, + entityType = SharedFSResponse.class, + description = "the ID of the shared filesystem") + private Long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } + + @Override + public String getEventDescription() { + return "Starting Shared FileSystem with ID: " + getResourceUuid(ApiConstants.ID); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_SHAREDFS_START; + } + + private String getStartExceptionMsg(Exception ex) { + return "Shared FileSystem start failed with exception: " + ex.getMessage(); + } + + @Override + public void execute() { + SharedFS sharedFS; + try { + sharedFS = sharedFSService.startSharedFS(this.getId()); + } catch (ResourceUnavailableException ex) { + logger.warn("Shared FileSystem start exception: ", ex); + throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, getStartExceptionMsg(ex)); + } catch (ConcurrentOperationException ex) { + logger.warn("Shared FileSystem start exception: ", ex); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, getStartExceptionMsg(ex)); + } catch (InsufficientCapacityException ex) { + logger.warn("Shared FileSystem start exception: ", ex); + throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, getStartExceptionMsg(ex)); + } catch (ResourceAllocationException ex) { + logger.warn("Shared FileSystem start exception: ", ex); + throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage()); + } catch (OperationTimedoutException ex) { + logger.warn("Shared FileSystem start exception: ", ex); + throw new CloudRuntimeException("Shared FileSystem start timed out due to " + ex.getMessage()); + } + + if (sharedFS != null) { + ResponseObject.ResponseView respView = getResponseView(); + Account caller = CallContext.current().getCallingAccount(); + if (_accountService.isRootAdmin(caller.getId())) { + respView = ResponseObject.ResponseView.Full; + } + SharedFSResponse response = _responseGenerator.createSharedFSResponse(respView, sharedFS); + response.setObjectName(SharedFS.class.getSimpleName().toLowerCase()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to start Shared FileSystem"); + } + } +} 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 new file mode 100644 index 000000000000..3800b16289e7 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/StopSharedFSCmd.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.storage.sharedfs; + +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.ResponseObject; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.SharedFSResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.storage.sharedfs.SharedFS; +import org.apache.cloudstack.storage.sharedfs.SharedFSService; + +import com.cloud.event.EventTypes; +import com.cloud.user.Account; + +@APICommand(name = "stopSharedFileSystem", + responseObject= SharedFSResponse.class, + description = "Stop a Shared FileSystem", + responseView = ResponseObject.ResponseView.Restricted, + entityType = SharedFS.class, + requestHasSensitiveInfo = false, + since = "4.20.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class StopSharedFSCmd extends BaseAsyncCmd implements UserCmd { + + @Inject + SharedFSService sharedFSService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, + type = CommandType.UUID, + required = true, + entityType = SharedFSResponse.class, + description = "the ID of the shared filesystem") + private Long id; + + @Parameter(name = ApiConstants.FORCED, + type = CommandType.BOOLEAN, + description = "Force stop the shared filesystem.") + private Boolean forced; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public boolean isForced() { + return (forced != null) ? forced : false; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_SHAREDFS_STOP; + } + + @Override + public String getEventDescription() { + return "Stopping Shared FileSystem with ID: " + getResourceUuid(ApiConstants.ID); + } + + @Override + public void execute() { + SharedFS sharedFS = sharedFSService.stopSharedFS(this.getId(), this.isForced()); + if (sharedFS != null) { + ResponseObject.ResponseView respView = getResponseView(); + Account caller = CallContext.current().getCallingAccount(); + if (_accountService.isRootAdmin(caller.getId())) { + respView = ResponseObject.ResponseView.Full; + } + SharedFSResponse response = _responseGenerator.createSharedFSResponse(respView, sharedFS); + response.setObjectName(SharedFS.class.getSimpleName().toLowerCase()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to stop Shared FileSystem"); + } + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/UpdateSharedFSCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/UpdateSharedFSCmd.java new file mode 100644 index 000000000000..daad6cc78c56 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/storage/sharedfs/UpdateSharedFSCmd.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.storage.sharedfs; + +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.ResponseObject; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.SharedFSResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.storage.sharedfs.SharedFS; +import org.apache.cloudstack.storage.sharedfs.SharedFSService; + +import javax.inject.Inject; + +import com.cloud.user.Account; + +@APICommand(name = "updateSharedFileSystem", + responseObject= SharedFSResponse.class, + description = "Update a Shared FileSystem", + responseView = ResponseObject.ResponseView.Restricted, + entityType = SharedFS.class, + requestHasSensitiveInfo = false, + since = "4.20.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class UpdateSharedFSCmd extends BaseCmd implements UserCmd { + + @Inject + SharedFSService sharedFSService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, + type = CommandType.UUID, + required = true, + entityType = SharedFSResponse.class, + description = "the ID of the shared filesystem") + private Long id; + + @Parameter(name = ApiConstants.NAME, + type = CommandType.STRING, + description = "the name of the shared filesystem.") + private String name; + + @Parameter(name = ApiConstants.DESCRIPTION, + type = CommandType.STRING, + description = "the description for the shared filesystem.") + private String description; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } + + @Override + public void execute() { + SharedFS sharedFS = sharedFSService.updateSharedFS(this); + if (sharedFS != null) { + ResponseObject.ResponseView respView = getResponseView(); + Account caller = CallContext.current().getCallingAccount(); + if (_accountService.isRootAdmin(caller.getId())) { + respView = ResponseObject.ResponseView.Full; + } + SharedFSResponse response = _responseGenerator.createSharedFSResponse(respView, sharedFS); + response.setObjectName(SharedFS.class.getSimpleName().toLowerCase()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update Shared FileSystem"); + } + } +} 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 0a7bf2918435..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 @@ -20,6 +20,7 @@ import java.util.List; import java.util.Map; +import com.cloud.cpu.CPU; import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; @@ -50,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"; @@ -65,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") @@ -126,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") @@ -148,6 +149,11 @@ public class CreateTemplateCmd extends BaseAsyncCreateCmd implements UserCmd { since = "4.19.0") private String accountName; + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + description = "the CPU arch of the template. Valid options are: x86_64, aarch64, s390x. Defaults to x86_64", + since = "4.20.2") + private String arch; + // /////////////////////////////////////////////////// // ///////////////// Accessors /////////////////////// // /////////////////////////////////////////////////// @@ -205,7 +211,7 @@ public String getUrl() { } public String getTemplateTag() { - return templateTag; + return StringUtils.isBlank(templateTag) ? null : templateTag; } public Map getDetails() { @@ -234,6 +240,10 @@ public String getAccountName() { return accountName; } + public CPU.CPUArch getArch() { + return CPU.CPUArch.fromType(arch); + } + // /////////////////////////////////////////////////// // ///////////// API Implementation/////////////////// // /////////////////////////////////////////////////// @@ -262,7 +272,7 @@ public String getEventType() { @Override public String getEventDescription() { - return "creating template: " + getTemplateName(); + return "Creating Template: " + getTemplateName(); } @Override @@ -283,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"); } } @@ -291,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) { @@ -308,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"); } } @@ -344,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 245baf1e07e0..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 @@ -18,6 +18,7 @@ import org.apache.commons.lang3.BooleanUtils; +import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -36,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 { @@ -44,15 +45,18 @@ 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}) + private Boolean isSystem; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -69,6 +73,10 @@ public boolean isForced() { return BooleanUtils.toBooleanDefaultIfNull(forced, false); } + public boolean getIsSystem() { + return BooleanUtils.toBooleanDefaultIfNull(isSystem, false); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -90,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 @@ -105,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 ce6ba5e300c1..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,7 +100,14 @@ public String getEventType() { @Override public String getEventDescription() { - return "extracting template: " + this._uuidMgr.getUuid(VirtualMachineTemplate.class, getId()) + ((getZoneId() != null) ? " from zone: " + this._uuidMgr.getUuid(DataCenter.class, getZoneId()) : ""); + String description = "Extracting Template with ID: " + getResourceUuid(ApiConstants.ID); + + Long zoneId = getZoneId(); + if (zoneId != null) { + description += "from zone with ID: " + getResourceUuid(ApiConstants.ZONE_ID); + } + + return description; } @Override @@ -120,11 +126,12 @@ public void execute() { CallContext.current().setEventDetails(getEventDescription()); String uploadUrl = _templateService.extract(this); if (uploadUrl != null) { - ExtractResponse response = _responseGenerator.createExtractResponse(id, zoneId, getEntityOwnerId(), mode, uploadUrl); + ExtractResponse response = _responseGenerator.createImageExtractResponse(id, zoneId, getEntityOwnerId(), mode, uploadUrl); response.setResponseName(getCommandName()); + 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 c878fda82409..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 @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.Map; +import com.cloud.cpu.CPU; import com.cloud.hypervisor.Hypervisor; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; @@ -37,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) @@ -45,54 +46,69 @@ 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, s390x", + since = "4.20") + private String arch; + @Parameter(name = ApiConstants.BITS, type = CommandType.INTEGER, description = "32 or 64 bits support. 64 by default") private Integer bits; @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, + type = CommandType.BOOLEAN, + description = "if true, the templates would be available for deploying CKS clusters", since = "4.21.0") + protected Boolean forCks; + + @Parameter(name = ApiConstants.TEMPLATE_TYPE, type = CommandType.STRING, + description = "the type of the template. Valid options are: USER/VNF (for all users) and SYSTEM/ROUTING/BUILTIN (for admins only).", + since = "4.22.0") + private String templateType; + public String getDisplayText() { return StringUtils.isBlank(displayText) ? getName() : displayText; } @@ -154,7 +170,7 @@ public Boolean isSshKeyEnabled() { } public String getTemplateTag() { - return templateTag; + return StringUtils.isBlank(templateTag) ? null : templateTag; } public boolean isDeployAsIs() { @@ -162,6 +178,18 @@ public boolean isDeployAsIs() { Boolean.TRUE.equals(deployAsIs); } + public boolean isForCks() { + return Boolean.TRUE.equals(forCks); + } + + public CPU.CPUArch getArch() { + return CPU.CPUArch.fromType(arch); + } + + public String getTemplateType() { + return templateType; + } + @Override public void execute() throws ServerApiException { validateRequest(); @@ -170,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"); @@ -195,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 113080257d02..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 @@ -16,16 +16,11 @@ // under the License. package org.apache.cloudstack.api.command.user.template; -import com.cloud.exception.InvalidParameterValueException; -import com.cloud.server.ResourceIcon; -import com.cloud.server.ResourceTag; -import org.apache.cloudstack.api.response.ResourceIconResponse; -import org.apache.commons.collections.CollectionUtils; - import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; import java.util.List; + import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -33,16 +28,23 @@ import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.ExtensionResponse; +import org.apache.cloudstack.api.response.GuestOSCategoryResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.TemplateResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import com.cloud.cpu.CPU; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.server.ResourceTag; import com.cloud.template.VirtualMachineTemplate; import com.cloud.template.VirtualMachineTemplate.TemplateFilter; import com.cloud.user.Account; -@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 { @@ -52,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, @@ -104,6 +106,29 @@ public class ListTemplatesCmd extends BaseListTaggedResourcesCmd implements User since = "4.19.0") private Boolean isVnf; + @Parameter(name = ApiConstants.FOR_CKS, type = CommandType.BOOLEAN, + description = "list templates that can be used to deploy CKS clusters", + since = "4.21.0") + private Boolean forCks; + + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + description = "the CPU arch of the template. Valid options are: x86_64, aarch64, s390x", + since = "4.20") + private String arch; + + @Parameter(name = ApiConstants.OS_CATEGORY_ID, type = CommandType.UUID, entityType = GuestOSCategoryResponse.class, + description = "the ID of the OS category for the template", + since = "4.21.0") + private Long osCategoryId; + + @Parameter(name = ApiConstants.EXTENSION_ID, type = CommandType.UUID, entityType = ExtensionResponse.class, + description = "ID of the extension for the template", + since = "4.21.0") + private Long extensionId; + + @Parameter(name = ApiConstants.IS_READY, type = CommandType.BOOLEAN, description = "list templates that are ready to be deployed", since = "4.21.0") + private Boolean ready; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -173,10 +198,17 @@ public boolean listInReadyState() { boolean onlyReady = (templateFilter == TemplateFilter.featured) || (templateFilter == TemplateFilter.selfexecutable) || (templateFilter == TemplateFilter.sharedexecutable) || (templateFilter == TemplateFilter.executable && isAccountSpecific) || (templateFilter == TemplateFilter.community); + + if (!onlyReady) { + if (isReady() != null && isReady().booleanValue() != onlyReady) { + onlyReady = isReady().booleanValue(); + } + } + return onlyReady; } - @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; ///////////////////////////////////////////////////// @@ -191,6 +223,27 @@ public Boolean getVnf() { return isVnf; } + public Boolean getForCks() { return forCks; } + + public CPU.CPUArch getArch() { + if (StringUtils.isBlank(arch)) { + return null; + } + return CPU.CPUArch.fromType(arch); + } + + public Long getOsCategoryId() { + return osCategoryId; + } + + public Long getExtensionId() { + return extensionId; + } + + public Boolean isReady() { + return ready; + } + @Override public String getCommandName() { return s_name; @@ -204,24 +257,14 @@ public ApiCommandResourceType getApiResourceType() { @Override public void execute() { ListResponse response = _queryService.listTemplates(this); - if (response != null && response.getCount() > 0 && getShowIcon()) { - updateTemplateResponse(response.getResponses()); + if (response != null && getShowIcon()) { + _responseGenerator.updateTemplateIsoResponsesForIcons(response.getResponses(), + ResourceTag.ResourceObjectType.Template); } response.setResponseName(getCommandName()); setResponseObject(response); } - private void updateTemplateResponse(List response) { - for (TemplateResponse templateResponse : response) { - ResourceIcon resourceIcon = resourceIconManager.getByResourceTypeAndUuid(ResourceTag.ResourceObjectType.Template, templateResponse.getId()); - if (resourceIcon == null) { - continue; - } - ResourceIconResponse iconResponse = _responseGenerator.createResourceIconResponse(resourceIcon); - templateResponse.setResourceIconResponse(iconResponse); - } - } - public List getIds() { if (ids == null) { return Collections.emptyList(); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java index 1e5c4af9c154..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 @@ -16,14 +16,12 @@ // under the License. package org.apache.cloudstack.api.command.user.template; -import com.cloud.hypervisor.Hypervisor; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; -import com.cloud.hypervisor.HypervisorGuru; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -34,6 +32,7 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.command.user.UserCmd; import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ExtensionResponse; import org.apache.cloudstack.api.response.GuestOSResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.ProjectResponse; @@ -42,10 +41,13 @@ import org.apache.cloudstack.context.CallContext; import org.apache.commons.lang3.StringUtils; +import com.cloud.cpu.CPU; import com.cloud.exception.ResourceAllocationException; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.hypervisor.HypervisorGuru; import com.cloud.template.VirtualMachineTemplate; -@APICommand(name = "registerTemplate", description = "Registers an existing template into the CloudStack cloud. ", responseObject = TemplateResponse.class, responseView = ResponseView.Restricted, +@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 { @@ -60,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, @@ -138,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, @@ -150,28 +152,46 @@ 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, + type = CommandType.BOOLEAN, + description = "if true, the templates would be available for deploying CKS clusters", since = "4.21.0") + protected Boolean forCks; + @Parameter(name = ApiConstants.TEMPLATE_TYPE, type = CommandType.STRING, description = "the type of the template. Valid options are: USER/VNF (for all users) and SYSTEM/ROUTING/BUILTIN (for admins only).", since = "4.19.0") private String templateType; + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + description = "the CPU arch of the template. Valid options are: x86_64, aarch64, s390x", + since = "4.20") + private String arch; + + @Parameter(name = ApiConstants.EXTENSION_ID, type = CommandType.UUID, entityType = ExtensionResponse.class, + description = "ID of the extension", + since = "4.21.0") + private Long extensionId; + + @Parameter(name = ApiConstants.EXTERNAL_DETAILS, type = CommandType.MAP, description = "Details in key/value pairs using format externaldetails[i].keyname=keyvalue. Example: externaldetails[0].endpoint.url=urlvalue", since = "4.21.0") + protected Map externalDetails; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -259,7 +279,7 @@ public String getChecksum() { } public String getTemplateTag() { - return templateTag; + return StringUtils.isBlank(templateTag) ? null : templateTag; } public Map getDetails() { @@ -289,10 +309,26 @@ public boolean isDeployAsIs() { Boolean.TRUE.equals(deployAsIs); } + public boolean isForCks() { + return Boolean.TRUE.equals(forCks); + } + public String getTemplateType() { return templateType; } + public CPU.CPUArch getArch() { + return CPU.CPUArch.fromType(arch); + } + + public Long getExtensionId() { + return extensionId; + } + + public Map getExternalDetails() { + return convertExternalDetailsToMap(externalDetails); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -308,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(); } @@ -330,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); @@ -352,10 +388,12 @@ protected void validateParameters() { "Parameter zoneids cannot combine all zones (-1) option with other zones"); String customHypervisor = HypervisorGuru.HypervisorCustomDisplayName.value(); - if (isDirectDownload() && !(getHypervisor().equalsIgnoreCase(Hypervisor.HypervisorType.KVM.toString()) + if (isDirectDownload() && + !(Hypervisor.HypervisorType.getType(getHypervisor()) + .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 3f11f3860b04..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 @@ -16,7 +16,6 @@ // under the License. package org.apache.cloudstack.api.command.user.template; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -31,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"; @@ -41,9 +40,17 @@ 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") + private String templateTag; + + @Parameter(name = ApiConstants.FOR_CKS, type = CommandType.BOOLEAN, + description = "indicates that the template can be used for deployment of CKS clusters", + since = "4.21.0") + private Boolean forCks; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -57,6 +64,14 @@ public String getTemplateType() { return templateType; } + public String getTemplateTag() { + return templateTag; + } + + public Boolean getForCks() { + return forCks; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -100,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/BaseRegisterUserDataCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/BaseRegisterUserDataCmd.java new file mode 100644 index 000000000000..c002bd226a0d --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/BaseRegisterUserDataCmd.java @@ -0,0 +1,87 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.userdata; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.NetworkModel; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.commons.lang3.StringUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public abstract class BaseRegisterUserDataCmd extends BaseCmd { + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Name of the user data") + private String name; + + //Owner information + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the user data. Must be used with domainId.") + private String accountName; + + @Parameter(name = ApiConstants.DOMAIN_ID, + type = CommandType.UUID, + entityType = DomainResponse.class, + description = "an optional domainId for the user data. If the account parameter is used, domainId must also be used.") + private Long domainId; + + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "an optional project for the user data") + private Long projectId; + + @Parameter(name = ApiConstants.PARAMS, type = CommandType.STRING, description = "comma separated list of variables declared in user data content") + private String params; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getName() { + return name; + } + + public String getAccountName() { + return accountName; + } + + public Long getDomainId() { + return domainId; + } + + public Long getProjectId() { + return projectId; + } + + public String getParams() { + checkForVRMetadataFileNames(params); + return params; + } + + public void checkForVRMetadataFileNames(String params) { + if (StringUtils.isNotEmpty(params)) { + List keyValuePairs = new ArrayList<>(Arrays.asList(params.split(","))); + keyValuePairs.retainAll(NetworkModel.metadataFileNames); + if (!keyValuePairs.isEmpty()) { + throw new InvalidParameterValueException(String.format("Params passed here have a few virtual router metadata file names %s", keyValuePairs)); + } + } + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/DeleteCniConfigurationCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/DeleteCniConfigurationCmd.java new file mode 100644 index 000000000000..8faa612d3d97 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/DeleteCniConfigurationCmd.java @@ -0,0 +1,74 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.userdata; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; + +import com.cloud.user.Account; +import com.cloud.user.UserData; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + + +@APICommand(name = "deleteCniConfiguration", description = "Deletes a CNI Configuration", responseObject = SuccessResponse.class, entityType = {UserData.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.21.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class DeleteCniConfigurationCmd extends DeleteUserDataCmd { + + public static final Logger logger = LogManager.getLogger(DeleteCniConfigurationCmd.class.getName()); + + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + boolean result = _mgr.deleteCniConfiguration(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setSuccess(result); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete CNI configuration"); + } + } + + @Override + public long getEntityOwnerId() { + Account account = CallContext.current().getCallingAccount(); + Long domainId = this.getDomainId(); + String accountName = this.getAccountName(); + if ((account == null || _accountService.isAdmin(account.getId())) && (domainId != null && accountName != null)) { + Account userAccount = _responseGenerator.findAccountByNameDomain(accountName, domainId); + if (userAccount != null) { + return userAccount.getId(); + } + } + + if (account != null) { + return account.getId(); + } + + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/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/ListCniConfigurationCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/ListCniConfigurationCmd.java new file mode 100644 index 000000000000..3a0c3f1168bc --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/ListCniConfigurationCmd.java @@ -0,0 +1,59 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.userdata; + +import com.cloud.user.UserData; +import com.cloud.utils.Pair; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.UserDataResponse; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.ArrayList; +import java.util.List; + +@APICommand(name = "listCniConfiguration", description = "List user data for CNI plugins", responseObject = UserDataResponse.class, entityType = {UserData.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.21.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class ListCniConfigurationCmd extends ListUserDataCmd { + public static final Logger logger = LogManager.getLogger(ListCniConfigurationCmd.class.getName()); + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + Pair, Integer> resultList = _mgr.listUserDatas(this, true); + List responses = new ArrayList<>(); + for (UserData result : resultList.first()) { + UserDataResponse r = _responseGenerator.createUserDataResponse(result); + r.setObjectName(ApiConstants.CNI_CONFIG); + responses.add(r); + } + + ListResponse response = new ListResponse<>(); + response.setResponses(responses, resultList.second()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/ListUserDataCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/ListUserDataCmd.java index 64ab3ec3d70e..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") @@ -61,7 +61,7 @@ public String getName() { @Override public void execute() { - Pair, Integer> resultList = _mgr.listUserDatas(this); + Pair, Integer> resultList = _mgr.listUserDatas(this, false); List responses = new ArrayList<>(); for (UserData result : resultList.first()) { UserDataResponse r = _responseGenerator.createUserDataResponse(result); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/RegisterCniConfigurationCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/RegisterCniConfigurationCmd.java new file mode 100644 index 000000000000..3f1de41eab89 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/RegisterCniConfigurationCmd.java @@ -0,0 +1,77 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.userdata; + +import com.cloud.user.UserData; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.UserDataResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +@APICommand(name = "registerCniConfiguration", + description = "Register a CNI Configuration to be used with CKS cluster", + since = "4.21.0", + responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class RegisterCniConfigurationCmd extends BaseRegisterUserDataCmd { + public static final Logger logger = LogManager.getLogger(RegisterCniConfigurationCmd.class.getName()); + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.CNI_CONFIG, type = CommandType.STRING, description = "CNI Configuration content to be registered as User data", length = 1048576) + private String cniConfig; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getCniConfig() { + return cniConfig; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + UserData result = _mgr.registerCniConfiguration(this); + UserDataResponse response = _responseGenerator.createUserDataResponse(result); + response.setResponseName(getCommandName()); + response.setObjectName(ApiConstants.CNI_CONFIG); + setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + Long accountId = _accountService.finalizeAccountId(getAccountName(), getDomainId(), getProjectId(), true); + if (accountId == null) { + return CallContext.current().getCallingAccount().getId(); + } + + return accountId; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/RegisterUserDataCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/RegisterUserDataCmd.java index 8df25541a197..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 @@ -16,115 +16,61 @@ // under the License. package org.apache.cloudstack.api.command.user.userdata; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.api.response.DomainResponse; -import org.apache.cloudstack.api.response.ProjectResponse; import org.apache.cloudstack.api.response.SuccessResponse; import org.apache.cloudstack.api.response.UserDataResponse; import org.apache.cloudstack.context.CallContext; -import org.apache.commons.lang3.StringUtils; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; -import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.NetworkRuleConflictException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; -import com.cloud.network.NetworkModel; import com.cloud.user.UserData; @APICommand(name = "registerUserData", - description = "Register a new userdata.", + description = "Register a new User Data.", since = "4.18", responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) -public class RegisterUserDataCmd extends BaseCmd { +public class RegisterUserDataCmd extends BaseRegisterUserDataCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Name of the userdata") - private String name; - - //Owner information - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the userdata. Must be used with domainId.") - private String accountName; - - @Parameter(name = ApiConstants.DOMAIN_ID, - type = CommandType.UUID, - entityType = DomainResponse.class, - description = "an optional domainId for the userdata. If the account parameter is used, domainId must also be used.") - private Long domainId; - - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "an optional project for the userdata") - private Long projectId; - - @Parameter(name = ApiConstants.USER_DATA, type = CommandType.STRING, required = true, description = "Userdata content", length = 1048576) - private String userData; - - @Parameter(name = ApiConstants.PARAMS, type = CommandType.STRING, description = "comma separated list of variables declared in userdata content") - private String params; - + @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 getName() { - return name; - } - - public String getAccountName() { - return accountName; - } - - public Long getDomainId() { - return domainId; - } - - public Long getProjectId() { - return projectId; - } - public String getUserData() { return userData; } - public String getParams() { - checkForVRMetadataFileNames(params); - return params; - } - - public void checkForVRMetadataFileNames(String params) { - if (StringUtils.isNotEmpty(params)) { - List keyValuePairs = new ArrayList<>(Arrays.asList(params.split(","))); - keyValuePairs.retainAll(NetworkModel.metadataFileNames); - if (!keyValuePairs.isEmpty()) { - throw new InvalidParameterValueException(String.format("Params passed here have a few virtual router metadata file names %s", keyValuePairs)); - } - } - } - ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.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 0dc3dcdbdcc8..6274e7e14963 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,7 +50,7 @@ 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") @@ -67,7 +67,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(); } @@ -79,7 +79,7 @@ public long getNicId() { private boolean isZoneSGEnabled() { Network ntwk = _entityMgr.findById(Network.class, getNetworkId()); DataCenter dc = _entityMgr.findById(DataCenter.class, ntwk.getDataCenterId()); - return dc.isSecurityGroupEnabled(); + return dc.isSecurityGroupEnabled() || _ntwkModel.isSecurityGroupSupportedForZone(dc.getId()); } @Override @@ -89,7 +89,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 +108,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 +142,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); @@ -166,11 +166,11 @@ public void create() throws ResourceAllocationException { 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..6347c38811e8 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; @@ -121,7 +120,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 +166,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 +176,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 new file mode 100644 index 000000000000..8c29d7338b85 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/BaseDeployVMCmd.java @@ -0,0 +1,846 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.vm; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nonnull; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.affinity.AffinityGroupResponse; +import org.apache.cloudstack.api.ACL; +import org.apache.cloudstack.api.ApiArgValidator; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiConstants.IoDriverPolicy; +import org.apache.cloudstack.api.BaseAsyncCreateCustomIdCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.DiskOfferingResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.api.response.SecurityGroupResponse; +import org.apache.cloudstack.api.response.UserDataResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.vm.lease.VMLeaseManager; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.StringUtils; + +import com.cloud.agent.api.LogLevel; +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.Network; +import com.cloud.network.Network.IpAddresses; +import com.cloud.offering.DiskOffering; +import com.cloud.template.VirtualMachineTemplate; +import com.cloud.utils.net.NetUtils; +import com.cloud.vm.VmDetailConstants; +import com.cloud.vm.VmDiskInfo; +import com.cloud.utils.net.Dhcp; + +public abstract class BaseDeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityGroupAction, UserCmd { + + private static final String s_name = "deployvirtualmachineresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "availability zone for the virtual machine") + private Long zoneId; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "host name for the virtual machine", validations = {ApiArgValidator.RFCComplianceDomainName}) + private String name; + + @Parameter(name = ApiConstants.DISPLAY_NAME, type = CommandType.STRING, description = "an optional user generated name for the virtual machine") + private String displayName; + + @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, description="The password of the virtual machine. If null, a random password will be generated for the VM.", + since="4.19.0.0") + protected String password; + + //Owner information + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the virtual machine. Must be used with domainId.") + private String accountName; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "an optional domainId for the virtual machine. If the account parameter is used, domainId must also be used. If account is NOT provided then virtual machine will be assigned to the caller account and domain.") + private Long domainId; + + //Network information + //@ACL(accessType = AccessType.UseEntry) + @Parameter(name = ApiConstants.NETWORK_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = NetworkResponse.class, description = "list of network ids used by virtual machine. Can't be specified with ipToNetworkList parameter") + private List networkIds; + + @Parameter(name = ApiConstants.BOOT_TYPE, type = CommandType.STRING, required = false, description = "Guest VM Boot option either custom[UEFI] or default boot [BIOS]. Not applicable with VMware if the template is marked as deploy-as-is, as we honour what is defined in the template.", since = "4.14.0.0") + private String bootType; + + @Parameter(name = ApiConstants.BOOT_MODE, type = CommandType.STRING, required = false, description = "Boot Mode [Legacy] or [Secure] Applicable when Boot Type Selected is UEFI, otherwise Legacy only for BIOS. Not applicable with VMware if the template is marked as deploy-as-is, as we honour what is defined in the template.", since = "4.14.0.0") + private String bootMode; + + @Parameter(name = ApiConstants.BOOT_INTO_SETUP, type = CommandType.BOOLEAN, required = false, description = "Boot into hardware setup or not (ignored if startVm = false, only valid for vmware)", since = "4.15.0.0") + private Boolean bootIntoSetup; + + //DataDisk information + @ACL + @Parameter(name = ApiConstants.DISK_OFFERING_ID, type = CommandType.UUID, entityType = DiskOfferingResponse.class, description = "the ID of the disk offering for the virtual machine. If the template is of ISO format," + + " the diskOfferingId is for the root disk volume. Otherwise this parameter is used to indicate the " + + "offering for the data disk volume. If the templateId parameter passed is from a Template object," + + " the diskOfferingId refers to a DATA Disk Volume created. If the templateId parameter passed is " + + "from an ISO object, the diskOfferingId refers to a ROOT Disk Volume created.") + private Long diskOfferingId; + + @Parameter(name = ApiConstants.SIZE, type = CommandType.LONG, description = "the arbitrary size for the DATADISK volume. Mutually exclusive with diskOfferingId") + private Long size; + + @Parameter(name = ApiConstants.ROOT_DISK_SIZE, + type = CommandType.LONG, + description = "Optional field to resize root disk on deploy. Value is in GB. Only applies to template-based deployments. Analogous to details[0].rootdisksize, which takes precedence over this parameter if both are provided", + since = "4.4") + private Long rootdisksize; + + @Parameter(name = ApiConstants.DATADISKS_DETAILS, + type = CommandType.MAP, + since = "4.21.0", + description = "Disk offering details for creating multiple data volumes. Mutually exclusive with diskOfferingId." + + " Example: datadisksdetails[0].diskofferingid=a2a73a84-19db-4852-8930-dfddef053341&datadisksdetails[0].size=10&datadisksdetails[0].miniops=100&datadisksdetails[0].maxiops=200") + private Map dataDisksDetails; + + @Parameter(name = ApiConstants.GROUP, type = CommandType.STRING, description = "an optional group for the virtual machine") + private String group; + + @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "the hypervisor on which to deploy the virtual machine. " + + "The parameter is required and respected only when hypervisor info is not set on the ISO/Template passed to the call") + private String hypervisor; + + @Parameter(name = ApiConstants.USER_DATA, type = CommandType.STRING, + description = "an optional binary data that can be sent to the virtual machine upon a successful deployment. " + + "This binary data must be base64 encoded before adding it to the request. " + + "Using HTTP GET (via querystring), you can send up to 4KB of data after base64 encoding. " + + "Using HTTP POST (via POST body), you can send up to 1MB of data after base64 encoding. " + + "You also need to change vm.userdata.max.length value", + length = 1048576) + private String userData; + + @Parameter(name = ApiConstants.USER_DATA_ID, type = CommandType.UUID, entityType = UserDataResponse.class, description = "the ID of the Userdata", since = "4.18") + private Long userdataId; + + @Parameter(name = ApiConstants.USER_DATA_DETAILS, type = CommandType.MAP, description = "used to specify the parameters values for the variables in userdata.", since = "4.18") + private Map userdataDetails; + + @Deprecated + @Parameter(name = ApiConstants.SSH_KEYPAIR, type = CommandType.STRING, description = "name of the ssh key pair used to login to the virtual machine") + private String sshKeyPairName; + + @Parameter(name = ApiConstants.SSH_KEYPAIRS, type = CommandType.LIST, collectionType = CommandType.STRING, since="4.17", description = "names of the ssh key pairs used to login to the virtual machine") + private List sshKeyPairNames; + + @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "destination Host ID to deploy the VM to - parameter available for root admin only") + private Long hostId; + + @ACL + @Parameter(name = ApiConstants.SECURITY_GROUP_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = SecurityGroupResponse.class, description = "comma separated list of security groups id that going to be applied to the virtual machine. " + + "Should be passed only when vm is created from a zone with Basic Network support." + " Mutually exclusive with securitygroupnames parameter") + private List securityGroupIdList; + + @ACL + @Parameter(name = ApiConstants.SECURITY_GROUP_NAMES, type = CommandType.LIST, collectionType = CommandType.STRING, entityType = SecurityGroupResponse.class, description = "comma separated list of security groups names that going to be applied to the virtual machine." + + " Should be passed only when vm is created from a zone with Basic Network support. " + "Mutually exclusive with securitygroupids parameter") + private List securityGroupNameList; + + @Parameter(name = ApiConstants.IP_NETWORK_LIST, type = CommandType.MAP, description = "ip to network mapping. Can't be specified with networkIds parameter." + + " Example: iptonetworklist[0].ip=10.10.10.11&iptonetworklist[0].ipv6=fc00:1234:5678::abcd&iptonetworklist[0].networkid=uuid&iptonetworklist[0].mac=aa:bb:cc:dd:ee::ff - requests to use ip 10.10.10.11 in network id=uuid") + private Map ipToNetworkList; + + @Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, description = "the ip address for default vm's network") + private String ipAddress; + + @Parameter(name = ApiConstants.IP6_ADDRESS, type = CommandType.STRING, description = "the ipv6 address for default vm's network") + private String ip6Address; + + @Parameter(name = ApiConstants.MAC_ADDRESS, type = CommandType.STRING, description = "the mac address for default vm's network") + private String macAddress; + + @Parameter(name = ApiConstants.KEYBOARD, type = CommandType.STRING, description = "an optional keyboard device type for the virtual machine. valid value can be one of de,de-ch,es,es-latam,fi,fr,fr-be,fr-ch,is,it,jp,nl-be,no,pt,uk,us") + private String keyboard; + + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "Deploy vm for the project") + private Long projectId; + + @Parameter(name = ApiConstants.START_VM, type = CommandType.BOOLEAN, description = "true if start vm after creating; defaulted to true if not specified") + private Boolean startVm; + + @ACL + @Parameter(name = ApiConstants.AFFINITY_GROUP_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = AffinityGroupResponse.class, description = "comma separated list of affinity groups id that are going to be applied to the virtual machine." + + " Mutually exclusive with affinitygroupnames parameter") + private List affinityGroupIdList; + + @ACL + @Parameter(name = ApiConstants.AFFINITY_GROUP_NAMES, type = CommandType.LIST, collectionType = CommandType.STRING, entityType = AffinityGroupResponse.class, description = "comma separated list of affinity groups names that are going to be applied to the virtual machine." + + "Mutually exclusive with affinitygroupids parameter") + private List affinityGroupNameList; + + @Parameter(name = ApiConstants.DISPLAY_VM, type = CommandType.BOOLEAN, since = "4.2", description = "an optional field, whether to the display the vm to the end user or not.", authorized = {RoleType.Admin}) + private Boolean displayVm; + + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, since = "4.3", description = "used to specify the custom parameters. 'extraconfig' is not allowed to be passed in details") + private Map details; + + @Parameter(name = ApiConstants.DEPLOYMENT_PLANNER, type = CommandType.STRING, description = "Deployment planner to use for vm allocation. Available to ROOT admin only", since = "4.4", authorized = { RoleType.Admin }) + private String deploymentPlanner; + + @Parameter(name = ApiConstants.DHCP_OPTIONS_NETWORK_LIST, type = CommandType.MAP, description = "DHCP options which are passed to the VM on start up" + + " Example: dhcpoptionsnetworklist[0].dhcp:114=url&dhcpoptionsetworklist[0].networkid=networkid&dhcpoptionsetworklist[0].dhcp:66=www.test.com") + private Map dhcpOptionsNetworkList; + + @Parameter(name = ApiConstants.DATADISK_OFFERING_LIST, type = CommandType.MAP, since = "4.11", description = "datadisk template to disk-offering mapping;" + + " an optional parameter used to create additional data disks from datadisk templates; can't be specified with diskOfferingId parameter") + private Map dataDiskTemplateToDiskOfferingList; + + @Parameter(name = ApiConstants.EXTRA_CONFIG, type = CommandType.STRING, since = "4.12", description = "an optional URL encoded string that can be passed to the virtual machine upon successful deployment", length = 5120) + private String extraConfig; + + @Parameter(name = ApiConstants.COPY_IMAGE_TAGS, type = CommandType.BOOLEAN, since = "4.13", description = "if true the image tags (if any) will be copied to the VM, default value is false") + private Boolean copyImageTags; + + @Parameter(name = ApiConstants.PROPERTIES, type = CommandType.MAP, since = "4.15", + description = "used to specify the vApp properties.") + @LogLevel(LogLevel.Log4jLevel.Off) + private Map vAppProperties; + + @Parameter(name = ApiConstants.NIC_NETWORK_LIST, type = CommandType.MAP, since = "4.15", + description = "VMware only: used to specify network mapping of a vApp VMware template registered \"as-is\"." + + " Example nicnetworklist[0].ip=Nic-101&nicnetworklist[0].network=uuid") + @LogLevel(LogLevel.Log4jLevel.Off) + private Map vAppNetworks; + + @Parameter(name = ApiConstants.DYNAMIC_SCALING_ENABLED, type = CommandType.BOOLEAN, since = "4.16", + description = "true if virtual machine needs to be dynamically scalable") + protected Boolean dynamicScalingEnabled; + + @Parameter(name = ApiConstants.OVERRIDE_DISK_OFFERING_ID, type = CommandType.UUID, since = "4.17", entityType = DiskOfferingResponse.class, description = "the ID of the disk offering for the virtual machine to be used for root volume instead of the disk offering mapped in service offering." + + "In case of virtual machine deploying from ISO, then the diskofferingid specified for root volume is ignored and uses this override disk offering id") + private Long overrideDiskOfferingId; + + @Parameter(name = ApiConstants.IOTHREADS_ENABLED, type = CommandType.BOOLEAN, required = false, + description = "IOThreads are dedicated event loop threads for supported disk devices to perform block I/O requests in order to improve scalability especially on an SMP host/guest with many LUNs.") + private Boolean iothreadsEnabled; + + @Parameter(name = ApiConstants.IO_DRIVER_POLICY, type = CommandType.STRING, description = "Controls specific policies on IO") + private String ioDriverPolicy; + + @Parameter(name = ApiConstants.NIC_MULTIQUEUE_NUMBER, type = CommandType.INTEGER, since = "4.18", + description = "The number of queues for multiqueue NICs.") + private Integer nicMultiqueueNumber; + + @Parameter(name = ApiConstants.NIC_PACKED_VIRTQUEUES_ENABLED, type = CommandType.BOOLEAN, since = "4.18", + description = "Enable packed virtqueues or not.") + private Boolean nicPackedVirtQueues; + + @Parameter(name = ApiConstants.INSTANCE_LEASE_DURATION, type = CommandType.INTEGER, since = "4.21.0", + description = "Number of days instance is leased for.") + private Integer leaseDuration; + + @Parameter(name = ApiConstants.INSTANCE_LEASE_EXPIRY_ACTION, type = CommandType.STRING, since = "4.21.0", + description = "Lease expiry action, valid values are STOP and DESTROY") + private String leaseExpiryAction; + + @Parameter(name = ApiConstants.EXTERNAL_DETAILS, + type = CommandType.MAP, + description = "Details in key/value pairs using format externaldetails[i].keyname=keyvalue. Example: externaldetails[0].server.type=typevalue", + since = "4.21.0") + protected Map externalDetails; + + private List dataDiskInfoList; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + public String getAccountName() { + if (accountName == null) { + return CallContext.current().getCallingAccount().getAccountName(); + } + return accountName; + } + + public Long getDiskOfferingId() { + return diskOfferingId; + } + + public String getDeploymentPlanner() { + return deploymentPlanner; + } + + public String getDisplayName() { + return displayName; + } + + public Long getDomainId() { + if (domainId == null) { + return CallContext.current().getCallingAccount().getDomainId(); + } + return domainId; + } + + public ApiConstants.BootType getBootType() { + if (StringUtils.isNotBlank(bootType)) { + try { + String type = bootType.trim().toUpperCase(); + return ApiConstants.BootType.valueOf(type); + } catch (IllegalArgumentException e) { + String errMesg = "Invalid bootType " + bootType + "Specified for vm " + getName() + + " Valid values are: " + Arrays.toString(ApiConstants.BootType.values()); + logger.warn(errMesg); + throw new InvalidParameterValueException(errMesg); + } + } + return null; + } + + public Map getDetails() { + Map customparameterMap = convertDetailsToMap(details); + + if (getBootType() != null) { + customparameterMap.put(getBootType().toString(), getBootMode().toString()); + } + + if (rootdisksize != null && !customparameterMap.containsKey(VmDetailConstants.ROOT_DISK_SIZE)) { + customparameterMap.put(VmDetailConstants.ROOT_DISK_SIZE, rootdisksize.toString()); + } + + IoDriverPolicy ioPolicy = getIoDriverPolicy(); + if (ioPolicy != null) { + customparameterMap.put(VmDetailConstants.IO_POLICY, ioPolicy.toString()); + } + + if (BooleanUtils.toBoolean(iothreadsEnabled)) { + customparameterMap.put(VmDetailConstants.IOTHREADS, BooleanUtils.toStringTrueFalse(iothreadsEnabled)); + } + + if (nicMultiqueueNumber != null) { + customparameterMap.put(VmDetailConstants.NIC_MULTIQUEUE_NUMBER, nicMultiqueueNumber.toString()); + } + + if (BooleanUtils.toBoolean(nicPackedVirtQueues)) { + customparameterMap.put(VmDetailConstants.NIC_PACKED_VIRTQUEUES_ENABLED, BooleanUtils.toStringTrueFalse(nicPackedVirtQueues)); + } + + if (MapUtils.isNotEmpty(externalDetails)) { + customparameterMap.putAll(getExternalDetails()); + } + return customparameterMap; + } + + public Map getExternalDetails() { + return convertExternalDetailsToMap(externalDetails); + } + + public ApiConstants.BootMode getBootMode() { + if (StringUtils.isNotBlank(bootMode)) { + try { + String mode = bootMode.trim().toUpperCase(); + return ApiConstants.BootMode.valueOf(mode); + } catch (IllegalArgumentException e) { + String msg = String.format("Invalid %s: %s specified for VM: %s. Valid values are: %s", + ApiConstants.BOOT_MODE, bootMode, getName(), Arrays.toString(ApiConstants.BootMode.values())); + logger.error(msg); + throw new InvalidParameterValueException(msg); + } + } + if (ApiConstants.BootType.UEFI.equals(getBootType())) { + String msg = String.format("%s must be specified for the VM with boot type: %s. Valid values are: %s", + ApiConstants.BOOT_MODE, getBootType(), Arrays.toString(ApiConstants.BootMode.values())); + logger.error(msg); + throw new InvalidParameterValueException(msg); + } + return null; + } + + public Map getVmProperties() { + Map map = new HashMap<>(); + if (MapUtils.isNotEmpty(vAppProperties)) { + Collection parameterCollection = vAppProperties.values(); + Iterator iterator = parameterCollection.iterator(); + while (iterator.hasNext()) { + HashMap entry = (HashMap)iterator.next(); + map.put(entry.get("key"), entry.get("value")); + } + } + return map; + } + + public Map getVmNetworkMap() { + Map map = new HashMap<>(); + if (MapUtils.isNotEmpty(vAppNetworks)) { + Collection parameterCollection = vAppNetworks.values(); + Iterator iterator = parameterCollection.iterator(); + while (iterator.hasNext()) { + HashMap entry = (HashMap) iterator.next(); + Integer nic; + try { + nic = Integer.valueOf(entry.get(VmDetailConstants.NIC)); + } catch (NumberFormatException nfe) { + nic = null; + } + String networkUuid = entry.get(VmDetailConstants.NETWORK); + 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)); + } + map.put(nic, _entityMgr.findByUuid(Network.class, networkUuid).getId()); + } + } + return map; + } + + public String getGroup() { + return group; + } + + public HypervisorType getHypervisor() { + return HypervisorType.getType(hypervisor); + } + + public Boolean isDisplayVm() { + return displayVm; + } + + @Override + public boolean isDisplay() { + if(displayVm == null) + return true; + else + return displayVm; + } + + public List getSecurityGroupNameList() { + return securityGroupNameList; + } + + public List getSecurityGroupIdList() { + return securityGroupIdList; + } + + public Long getSize() { + return size; + } + + public String getUserData() { + return userData; + } + + public Long getUserdataId() { + return userdataId; + } + + public Map getUserdataDetails() { + return convertDetailsToMap(userdataDetails); + } + + public Long getZoneId() { + return zoneId; + } + + public String getPassword() { + return password; + } + + public Integer getLeaseDuration() { + return leaseDuration; + } + + public VMLeaseManager.ExpiryAction getLeaseExpiryAction() { + if (StringUtils.isBlank(leaseExpiryAction)) { + return null; + } + VMLeaseManager.ExpiryAction action = EnumUtils.getEnumIgnoreCase(VMLeaseManager.ExpiryAction.class, leaseExpiryAction); + if (action == null) { + throw new InvalidParameterValueException("Invalid value configured for leaseexpiryaction, valid values are: " + + com.cloud.utils.EnumUtils.listValues(VMLeaseManager.ExpiryAction.values())); + } + return action; + } + + public List getNetworkIds() { + if (MapUtils.isNotEmpty(vAppNetworks)) { + if (CollectionUtils.isNotEmpty(networkIds) || ipAddress != null || getIp6Address() != null || MapUtils.isNotEmpty(ipToNetworkList)) { + throw new InvalidParameterValueException(String.format("%s can't be specified along with %s, %s, %s", ApiConstants.NIC_NETWORK_LIST, ApiConstants.NETWORK_IDS, ApiConstants.IP_ADDRESS, ApiConstants.IP_NETWORK_LIST)); + } else { + return new ArrayList<>(); + } + } + if (ipToNetworkList != null && !ipToNetworkList.isEmpty()) { + if ((networkIds != null && !networkIds.isEmpty()) || ipAddress != null || getIp6Address() != null) { + throw new InvalidParameterValueException("ipToNetworkMap can't be specified along with networkIds or ipAddress"); + } else { + List networks = new ArrayList(); + networks.addAll(getIpToNetworkMap().keySet()); + return networks; + } + } + return networkIds; + } + + public String getName() { + return name; + } + + public List getSSHKeyPairNames() { + List sshKeyPairs = new ArrayList(); + if(sshKeyPairNames != null) { + sshKeyPairs = sshKeyPairNames; + } + if(sshKeyPairName != null && !sshKeyPairName.isEmpty()) { + sshKeyPairs.add(sshKeyPairName); + } + return sshKeyPairs; + } + + public List getDataDiskInfoList() { + if (this.dataDiskInfoList != null) { + return this.dataDiskInfoList; + } + if (dataDisksDetails == null || dataDisksDetails.isEmpty()) { + return null; + } + if (dataDiskTemplateToDiskOfferingList != null) { + throw new InvalidParameterValueException("datadisktemplatetodiskofferinglist parameter can't be specified along with datadisksdetails parameter"); + } + List vmDiskInfoList = new ArrayList<>(); + Collection dataDisksCollection = dataDisksDetails.values(); + Iterator iter = dataDisksCollection.iterator(); + while (iter.hasNext()) { + HashMap dataDisk = (HashMap)iter.next(); + String diskOfferingUuid = dataDisk.get(ApiConstants.DISK_OFFERING_ID); + if (diskOfferingUuid == null) { + throw new InvalidParameterValueException("diskofferingid parameter is required for datadiskdetails"); + } + DiskOffering diskOffering = _entityMgr.findByUuid(DiskOffering.class, diskOfferingUuid); + if (diskOffering == null) { + throw new InvalidParameterValueException("Unable to find disk offering " + diskOfferingUuid); + } + if (diskOffering.isComputeOnly()) { + throw new InvalidParameterValueException(String.format("The disk offering id %d provided is directly mapped to a service offering, please provide an individual disk offering", diskOffering.getUuid())); + } + + Long size = null; + Long minIops = null; + Long maxIops = null; + if (dataDisk.get(ApiConstants.DEVICE_ID) == null) { + throw new InvalidParameterValueException("deviceid parameter is required for datadiskdetails"); + } + Long deviceId = Long.parseLong(dataDisk.get(ApiConstants.DEVICE_ID)); + if (diskOffering.isCustomized()) { + if (dataDisk.get(ApiConstants.SIZE) == null) { + throw new InvalidParameterValueException("Size is required for custom disk offering"); + } + size = Long.parseLong(dataDisk.get(ApiConstants.SIZE)); + } else { + size = diskOffering.getDiskSize() / (1024 * 1024 * 1024); + } + if (diskOffering.isCustomizedIops() != null && diskOffering.isCustomizedIops()) { + if (dataDisk.get(ApiConstants.MIN_IOPS) == null) { + throw new InvalidParameterValueException("Min IOPS is required for custom disk offering"); + } + if (dataDisk.get(ApiConstants.MAX_IOPS) == null) { + throw new InvalidParameterValueException("Max IOPS is required for custom disk offering"); + } + minIops = Long.parseLong(dataDisk.get(ApiConstants.MIN_IOPS)); + maxIops = Long.parseLong(dataDisk.get(ApiConstants.MAX_IOPS)); + } + VmDiskInfo vmDiskInfo = new VmDiskInfo(diskOffering, size, minIops, maxIops, deviceId); + vmDiskInfoList.add(vmDiskInfo); + } + this.dataDiskInfoList = vmDiskInfoList; + return dataDiskInfoList; + } + + public Long getHostId() { + return hostId; + } + + public boolean getStartVm() { + return startVm == null ? true : startVm; + } + + public Map getIpToNetworkMap() { + if ((networkIds != null || ipAddress != null || getIp6Address() != null) && ipToNetworkList != null) { + throw new InvalidParameterValueException("NetworkIds and ipAddress can't be specified along with ipToNetworkMap parameter"); + } + LinkedHashMap ipToNetworkMap = null; + if (ipToNetworkList != null && !ipToNetworkList.isEmpty()) { + ipToNetworkMap = new LinkedHashMap(); + Collection ipsCollection = ipToNetworkList.values(); + Iterator iter = ipsCollection.iterator(); + while (iter.hasNext()) { + HashMap ips = (HashMap)iter.next(); + Long networkId = getNetworkIdFomIpMap(ips); + IpAddresses addrs = getIpAddressesFromIpMap(ips); + ipToNetworkMap.put(networkId, addrs); + } + } + + return ipToNetworkMap; + } + + @Nonnull + private IpAddresses getIpAddressesFromIpMap(HashMap ips) { + String requestedIp = ips.get("ip"); + String requestedIpv6 = ips.get("ipv6"); + String requestedMac = ips.get("mac"); + if (requestedIpv6 != null) { + requestedIpv6 = NetUtils.standardizeIp6Address(requestedIpv6); + } + if (requestedMac != null) { + if(!NetUtils.isValidMac(requestedMac)) { + throw new InvalidParameterValueException("Mac address is not valid: " + requestedMac); + } else if(!NetUtils.isUnicastMac(requestedMac)) { + throw new InvalidParameterValueException("Mac address is not unicast: " + requestedMac); + } + requestedMac = NetUtils.standardizeMacAddress(requestedMac); + } + return new IpAddresses(requestedIp, requestedIpv6, requestedMac); + } + + @Nonnull + private Long getNetworkIdFomIpMap(HashMap ips) { + Long networkId; + final String networkid = ips.get("networkid"); + Network network = _networkService.getNetwork(networkid); + if (network != null) { + networkId = network.getId(); + } else { + try { + networkId = Long.parseLong(networkid); + } catch (NumberFormatException e) { + throw new InvalidParameterValueException("Unable to translate and find entity with networkId: " + networkid); + } + } + return networkId; + } + + public String getIpAddress() { + return ipAddress; + } + + public String getIp6Address() { + if (ip6Address == null) { + return null; + } + return NetUtils.standardizeIp6Address(ip6Address); + } + + + public String getMacAddress() { + if (macAddress == null) { + return null; + } + if(!NetUtils.isValidMac(macAddress)) { + throw new InvalidParameterValueException("Mac address is not valid: " + macAddress); + } else if(!NetUtils.isUnicastMac(macAddress)) { + throw new InvalidParameterValueException("Mac address is not unicast: " + macAddress); + } + return NetUtils.standardizeMacAddress(macAddress); + } + + public List getAffinityGroupIdList() { + if (affinityGroupNameList != null && affinityGroupIdList != null) { + throw new InvalidParameterValueException("affinitygroupids parameter is mutually exclusive with affinitygroupnames parameter"); + } + + // transform group names to ids here + if (affinityGroupNameList != null) { + List affinityGroupIds = new ArrayList(); + for (String groupName : affinityGroupNameList) { + Long groupId = _responseGenerator.getAffinityGroupId(groupName, getEntityOwnerId()); + if (groupId == null) { + throw new InvalidParameterValueException("Unable to find affinity group by name " + groupName); + } else { + affinityGroupIds.add(groupId); + } + } + return affinityGroupIds; + } else { + return affinityGroupIdList; + } + } + + public String getKeyboard() { + // TODO Auto-generated method stub + return keyboard; + } + + public Map> getDhcpOptionsMap() { + Map> dhcpOptionsMap = new HashMap<>(); + if (dhcpOptionsNetworkList != null && !dhcpOptionsNetworkList.isEmpty()) { + + Collection> paramsCollection = this.dhcpOptionsNetworkList.values(); + for (Map dhcpNetworkOptions : paramsCollection) { + String networkId = dhcpNetworkOptions.get(ApiConstants.NETWORK_ID); + + if (networkId == null) { + throw new IllegalArgumentException("No networkid specified when providing extra dhcp options."); + } + + Map dhcpOptionsForNetwork = new HashMap<>(); + dhcpOptionsMap.put(networkId, dhcpOptionsForNetwork); + + for (String key : dhcpNetworkOptions.keySet()) { + if (key.startsWith(ApiConstants.DHCP_PREFIX)) { + int dhcpOptionValue = Integer.parseInt(key.replaceFirst(ApiConstants.DHCP_PREFIX, "")); + dhcpOptionsForNetwork.put(dhcpOptionValue, dhcpNetworkOptions.get(key)); + } else if (!key.equals(ApiConstants.NETWORK_ID)) { + Dhcp.DhcpOptionCode dhcpOptionEnum = Dhcp.DhcpOptionCode.valueOfString(key); + dhcpOptionsForNetwork.put(dhcpOptionEnum.getCode(), dhcpNetworkOptions.get(key)); + } + } + + } + } + + return dhcpOptionsMap; + } + + public Map getDataDiskTemplateToDiskOfferingMap() { + if (diskOfferingId != null && dataDiskTemplateToDiskOfferingList != null) { + throw new InvalidParameterValueException("diskofferingid parameter can't be specified along with datadisktemplatetodiskofferinglist parameter"); + } + if (MapUtils.isEmpty(dataDiskTemplateToDiskOfferingList)) { + return new HashMap(); + } + + HashMap dataDiskTemplateToDiskOfferingMap = new HashMap(); + for (Object objDataDiskTemplates : dataDiskTemplateToDiskOfferingList.values()) { + HashMap dataDiskTemplates = (HashMap) objDataDiskTemplates; + Long dataDiskTemplateId; + DiskOffering dataDiskOffering = null; + VirtualMachineTemplate dataDiskTemplate= _entityMgr.findByUuid(VirtualMachineTemplate.class, dataDiskTemplates.get("datadisktemplateid")); + if (dataDiskTemplate == null) { + dataDiskTemplate = _entityMgr.findById(VirtualMachineTemplate.class, dataDiskTemplates.get("datadisktemplateid")); + if (dataDiskTemplate == null) + throw new InvalidParameterValueException("Unable to translate and find entity with datadisktemplateid " + dataDiskTemplates.get("datadisktemplateid")); + } + dataDiskTemplateId = dataDiskTemplate.getId(); + dataDiskOffering = _entityMgr.findByUuid(DiskOffering.class, dataDiskTemplates.get("diskofferingid")); + if (dataDiskOffering == null) { + dataDiskOffering = _entityMgr.findById(DiskOffering.class, dataDiskTemplates.get("diskofferingid")); + if (dataDiskOffering == null) + throw new InvalidParameterValueException("Unable to translate and find entity with diskofferingId " + dataDiskTemplates.get("diskofferingid")); + } + dataDiskTemplateToDiskOfferingMap.put(dataDiskTemplateId, dataDiskOffering); + } + return dataDiskTemplateToDiskOfferingMap; + } + + public String getExtraConfig() { + return extraConfig; + } + + public boolean getCopyImageTags() { + return copyImageTags == null ? false : copyImageTags; + } + + public Boolean getBootIntoSetup() { + return bootIntoSetup; + } + + public boolean isDynamicScalingEnabled() { + return dynamicScalingEnabled == null ? true : dynamicScalingEnabled; + } + + public Long getOverrideDiskOfferingId() { + return overrideDiskOfferingId; + } + + public IoDriverPolicy getIoDriverPolicy() { + if (StringUtils.isNotBlank(ioDriverPolicy)) { + try { + String policyType = ioDriverPolicy.trim().toUpperCase(); + return IoDriverPolicy.valueOf(policyType); + } catch (IllegalArgumentException e) { + String errMesg = String.format("Invalid io policy %s specified for vm %s. Valid values are: %s", ioDriverPolicy, getName(), Arrays.toString(IoDriverPolicy.values())); + logger.warn(errMesg); + throw new InvalidParameterValueException(errMesg); + } + } + return null; + } + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + public static String getResultObjectName() { + return "virtualmachine"; + } + + @Override + public long getEntityOwnerId() { + Long accountId = _accountService.finalizeAccountId(accountName, domainId, projectId, true); + if (accountId == null) { + return CallContext.current().getCallingAccount().getId(); + } + + return accountId; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_VM_CREATE; + } + + @Override + public String getCreateEventType() { + return EventTypes.EVENT_VM_CREATE; + } + + @Override + public String getCreateEventDescription() { + return "Creating Instance"; + } + + @Override + public String getEventDescription() { + if(getStartVm()) { + return "Starting Instance with ID: " + getEntityUuid(); + } + return "Deploying Instance with ID: " + getEntityUuid(); + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.VirtualMachine; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/CreateVMFromBackupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/CreateVMFromBackupCmd.java new file mode 100644 index 000000000000..e17ba9c2d705 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/CreateVMFromBackupCmd.java @@ -0,0 +1,153 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.vm; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.ACL; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.BackupResponse; +import org.apache.cloudstack.api.response.ServiceOfferingResponse; +import org.apache.cloudstack.api.response.TemplateResponse; +import org.apache.cloudstack.api.response.UserVmResponse; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InsufficientServerCapacityException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.uservm.UserVm; +import com.cloud.vm.VirtualMachine; + +@APICommand(name = "createVMFromBackup", + description = "Creates and automatically starts a VM from a backup.", + responseObject = UserVmResponse.class, + responseView = ResponseObject.ResponseView.Restricted, + entityType = {VirtualMachine.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = true, + since = "4.21.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class CreateVMFromBackupCmd extends BaseDeployVMCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.BACKUP_ID, + type = CommandType.UUID, + entityType = BackupResponse.class, + required = true, + description = "backup ID to create the VM from") + private Long backupId; + + @ACL + @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, description = "the ID of the service offering for the virtual machine") + private Long serviceOfferingId; + + @ACL + @Parameter(name = ApiConstants.TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, description = "the ID of the template for the virtual machine") + private Long templateId; + + @Parameter(name = ApiConstants.PRESERVE_IP, type = CommandType.BOOLEAN, description = "Use the same IP/MAC addresses as stored in the backup metadata. Works only if the original Instance is deleted and the IP/MAC address is available.") + private Boolean preserveIp; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getBackupId() { + return backupId; + } + + public Long getServiceOfferingId() { + return serviceOfferingId; + } + + public Long getTemplateId() { + return templateId; + } + + public boolean getPreserveIp() { + return (preserveIp != null) ? preserveIp : false; + } + + @Override + public void create() { + UserVm vm; + try { + vm = _userVmService.allocateVMFromBackup(this); + if (vm != null) { + setEntityId(vm.getId()); + setEntityUuid(vm.getUuid()); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to deploy vm"); + } + } catch (InsufficientCapacityException ex) { + logger.info(ex); + logger.trace(ex.getMessage(), ex); + throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, ex.getMessage()); + } catch (ResourceUnavailableException ex) { + logger.warn("Exception: ", ex); + throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage()); + } catch (ConcurrentOperationException ex) { + logger.warn("Exception: ", ex); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } catch (ResourceAllocationException ex) { + logger.warn("Exception: ", ex); + throw new ServerApiException(ApiErrorCode.RESOURCE_ALLOCATION_ERROR, ex.getMessage()); + } + } + + @Override + public void execute () { + UserVm vm = null; + try { + vm = _userVmService.restoreVMFromBackup(this); + } catch (ResourceUnavailableException ex) { + logger.warn("Exception: ", ex); + throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage()); + } catch (ResourceAllocationException ex) { + logger.warn("Exception: ", ex); + throw new ServerApiException(ApiErrorCode.RESOURCE_ALLOCATION_ERROR, ex.getMessage()); + } catch (ConcurrentOperationException ex) { + logger.warn("Exception: ", ex); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } catch (InsufficientCapacityException ex) { + StringBuilder message = new StringBuilder(ex.getMessage()); + if (ex instanceof InsufficientServerCapacityException) { + if (((InsufficientServerCapacityException)ex).isAffinityApplied()) { + message.append(", Please check the affinity groups provided, there may not be sufficient capacity to follow them"); + } + } + logger.info("{}: {}", message.toString(), ex.getLocalizedMessage()); + logger.debug(message.toString(), ex); + throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, message.toString()); + } + + if (vm != null) { + UserVmResponse response = _responseGenerator.createUserVmResponse(getResponseView(), "virtualmachine", vm).get(0); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to deploy vm uuid:"+getEntityUuid()); + } + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/CreateVMScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/CreateVMScheduleCmd.java index 935f39bf4ddb..7e9bdd942ed7 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 @@ -32,7 +32,7 @@ import javax.inject.Inject; import java.util.Date; -@APICommand(name = "createVMSchedule", description = "Create VM Schedule", responseObject = VMScheduleResponse.class, +@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 { @@ -44,7 +44,7 @@ public class CreateVMScheduleCmd 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.DESCRIPTION, @@ -56,7 +56,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,27 +68,27 @@ public class CreateVMScheduleCmd extends BaseCmd { @Parameter(name = ApiConstants.ACTION, type = CommandType.STRING, required = true, - description = "Action to take on the VM (start/stop/restart/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." + description = "Start date from which the schedule becomes active. Defaults to current date plus 1 minute." + "Use 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" + description = "End date after which the schedule becomes inactive" + "Use 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; ///////////////////////////////////////////////////// @@ -145,7 +145,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..f34d07b045d9 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 @@ -37,7 +37,7 @@ import java.util.Collections; import java.util.List; -@APICommand(name = "deleteVMSchedule", description = "Delete VM Schedule.", responseObject = SuccessResponse.class, +@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 { @@ -48,20 +48,20 @@ public class DeleteVMScheduleCmd extends BaseCmd { 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; ///////////////////////////////////////////////////// @@ -97,7 +97,7 @@ public void execute() { response.setObjectName(VMSchedule.class.getSimpleName().toLowerCase()); 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 +105,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 446bdf30f07a..050592b97a3b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java @@ -16,761 +16,83 @@ // under the License. package org.apache.cloudstack.api.command.user.vm; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; -import javax.annotation.Nonnull; - -import org.apache.cloudstack.acl.RoleType; -import org.apache.cloudstack.affinity.AffinityGroupResponse; +import com.cloud.utils.exception.CloudRuntimeException; 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.ApiConstants.IoDriverPolicy; import org.apache.cloudstack.api.ApiErrorCode; -import org.apache.cloudstack.api.BaseAsyncCreateCustomIdCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.api.command.user.UserCmd; -import org.apache.cloudstack.api.response.DiskOfferingResponse; -import org.apache.cloudstack.api.response.DomainResponse; -import org.apache.cloudstack.api.response.HostResponse; -import org.apache.cloudstack.api.response.NetworkResponse; -import org.apache.cloudstack.api.response.ProjectResponse; -import org.apache.cloudstack.api.response.SecurityGroupResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; +import org.apache.cloudstack.api.response.SnapshotResponse; import org.apache.cloudstack.api.response.TemplateResponse; -import org.apache.cloudstack.api.response.UserDataResponse; import org.apache.cloudstack.api.response.UserVmResponse; -import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.api.response.VolumeResponse; import org.apache.cloudstack.context.CallContext; -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.collections.MapUtils; -import org.apache.commons.lang3.BooleanUtils; -import org.apache.commons.lang3.StringUtils; -import com.cloud.agent.api.LogLevel; -import com.cloud.event.EventTypes; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InsufficientServerCapacityException; -import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; -import com.cloud.hypervisor.Hypervisor.HypervisorType; -import com.cloud.network.Network; -import com.cloud.network.Network.IpAddresses; -import com.cloud.offering.DiskOffering; -import com.cloud.template.VirtualMachineTemplate; import com.cloud.uservm.UserVm; -import com.cloud.utils.net.Dhcp; -import com.cloud.utils.net.NetUtils; import com.cloud.vm.VirtualMachine; -import com.cloud.vm.VmDetailConstants; - -@APICommand(name = "deployVirtualMachine", description = "Creates and automatically starts a virtual machine based on a service offering, disk offering, and template.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, - requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) -public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityGroupAction, UserCmd { - private static final String s_name = "deployvirtualmachineresponse"; +@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 { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "availability zone for the virtual machine") - private Long zoneId; - @ACL - @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, required = true, description = "the ID of the service offering for the virtual machine") + @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, required = true, description = "the ID of the template for the virtual machine") + @Parameter(name = ApiConstants.TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, description = "The ID of the Template for the Instance") private Long templateId; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "host name for the virtual machine") - private String name; - - @Parameter(name = ApiConstants.DISPLAY_NAME, type = CommandType.STRING, description = "an optional user generated name for the virtual machine") - private String displayName; - - @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, description="The password of the virtual machine. If null, a random password will be generated for the VM.", - since="4.19.0.0") - protected String password; - - //Owner information - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the virtual machine. Must be used with domainId.") - private String accountName; - - @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "an optional domainId for the virtual machine. If the account parameter is used, domainId must also be used. If account is NOT provided then virtual machine will be assigned to the caller account and domain.") - private Long domainId; - - //Network information - //@ACL(accessType = AccessType.UseEntry) - @Parameter(name = ApiConstants.NETWORK_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = NetworkResponse.class, description = "list of network ids used by virtual machine. Can't be specified with ipToNetworkList parameter") - private List networkIds; - - @Parameter(name = ApiConstants.BOOT_TYPE, type = CommandType.STRING, required = false, description = "Guest VM Boot option either custom[UEFI] or default boot [BIOS]. Not applicable with VMware if the template is marked as deploy-as-is, as we honour what is defined in the template.", since = "4.14.0.0") - private String bootType; - - @Parameter(name = ApiConstants.BOOT_MODE, type = CommandType.STRING, required = false, description = "Boot Mode [Legacy] or [Secure] Applicable when Boot Type Selected is UEFI, otherwise Legacy only for BIOS. Not applicable with VMware if the template is marked as deploy-as-is, as we honour what is defined in the template.", since = "4.14.0.0") - private String bootMode; - - @Parameter(name = ApiConstants.BOOT_INTO_SETUP, type = CommandType.BOOLEAN, required = false, description = "Boot into hardware setup or not (ignored if startVm = false, only valid for vmware)", since = "4.15.0.0") - private Boolean bootIntoSetup; - - //DataDisk information - @ACL - @Parameter(name = ApiConstants.DISK_OFFERING_ID, type = CommandType.UUID, entityType = DiskOfferingResponse.class, description = "the ID of the disk offering for the virtual machine. If the template is of ISO format," - + " the diskOfferingId is for the root disk volume. Otherwise this parameter is used to indicate the " - + "offering for the data disk volume. If the templateId parameter passed is from a Template object," - + " the diskOfferingId refers to a DATA Disk Volume created. If the templateId parameter passed is " - + "from an ISO object, the diskOfferingId refers to a ROOT Disk Volume created.") - private Long diskOfferingId; - - @Parameter(name = ApiConstants.SIZE, type = CommandType.LONG, description = "the arbitrary size for the DATADISK volume. Mutually exclusive with diskOfferingId") - private Long size; - - @Parameter(name = ApiConstants.ROOT_DISK_SIZE, - type = CommandType.LONG, - description = "Optional field to resize root disk on deploy. Value is in GB. Only applies to template-based deployments. Analogous to details[0].rootdisksize, which takes precedence over this parameter if both are provided", - since = "4.4") - private Long rootdisksize; - - @Parameter(name = ApiConstants.GROUP, type = CommandType.STRING, description = "an optional group for the virtual machine") - private String group; - - @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "the hypervisor on which to deploy the virtual machine. " - + "The parameter is required and respected only when hypervisor info is not set on the ISO/Template passed to the call") - private String hypervisor; - - @Parameter(name = ApiConstants.USER_DATA, type = CommandType.STRING, - description = "an optional binary data that can be sent to the virtual machine upon a successful deployment. This binary data must be base64 encoded before adding it to the request. Using HTTP GET (via querystring), you can send up to 4KB of data after base64 encoding. Using HTTP POST(via POST body), you can send up to 1MB of data after base64 encoding.", - length = 1048576) - private String userData; - - @Parameter(name = ApiConstants.USER_DATA_ID, type = CommandType.UUID, entityType = UserDataResponse.class, description = "the ID of the Userdata", since = "4.18") - private Long userdataId; - - @Parameter(name = ApiConstants.USER_DATA_DETAILS, type = CommandType.MAP, description = "used to specify the parameters values for the variables in userdata.", since = "4.18") - private Map userdataDetails; - - @Deprecated - @Parameter(name = ApiConstants.SSH_KEYPAIR, type = CommandType.STRING, description = "name of the ssh key pair used to login to the virtual machine") - private String sshKeyPairName; - - @Parameter(name = ApiConstants.SSH_KEYPAIRS, type = CommandType.LIST, collectionType = CommandType.STRING, since="4.17", description = "names of the ssh key pairs used to login to the virtual machine") - private List sshKeyPairNames; - - @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "destination Host ID to deploy the VM to - parameter available for root admin only") - private Long hostId; - - @ACL - @Parameter(name = ApiConstants.SECURITY_GROUP_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = SecurityGroupResponse.class, description = "comma separated list of security groups id that going to be applied to the virtual machine. " - + "Should be passed only when vm is created from a zone with Basic Network support." + " Mutually exclusive with securitygroupnames parameter") - private List securityGroupIdList; - - @ACL - @Parameter(name = ApiConstants.SECURITY_GROUP_NAMES, type = CommandType.LIST, collectionType = CommandType.STRING, entityType = SecurityGroupResponse.class, description = "comma separated list of security groups names that going to be applied to the virtual machine." - + " Should be passed only when vm is created from a zone with Basic Network support. " + "Mutually exclusive with securitygroupids parameter") - private List securityGroupNameList; - - @Parameter(name = ApiConstants.IP_NETWORK_LIST, type = CommandType.MAP, description = "ip to network mapping. Can't be specified with networkIds parameter." - + " Example: iptonetworklist[0].ip=10.10.10.11&iptonetworklist[0].ipv6=fc00:1234:5678::abcd&iptonetworklist[0].networkid=uuid&iptonetworklist[0].mac=aa:bb:cc:dd:ee::ff - requests to use ip 10.10.10.11 in network id=uuid") - private Map ipToNetworkList; - - @Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, description = "the ip address for default vm's network") - private String ipAddress; - - @Parameter(name = ApiConstants.IP6_ADDRESS, type = CommandType.STRING, description = "the ipv6 address for default vm's network") - private String ip6Address; + @Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.UUID, entityType = VolumeResponse.class, since = "4.21") + private Long volumeId; - @Parameter(name = ApiConstants.MAC_ADDRESS, type = CommandType.STRING, description = "the mac address for default vm's network") - private String macAddress; - - @Parameter(name = ApiConstants.KEYBOARD, type = CommandType.STRING, description = "an optional keyboard device type for the virtual machine. valid value can be one of de,de-ch,es,fi,fr,fr-be,fr-ch,is,it,jp,nl-be,no,pt,uk,us") - private String keyboard; - - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "Deploy vm for the project") - private Long projectId; - - @Parameter(name = ApiConstants.START_VM, type = CommandType.BOOLEAN, description = "true if start vm after creating; defaulted to true if not specified") - private Boolean startVm; - - @ACL - @Parameter(name = ApiConstants.AFFINITY_GROUP_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = AffinityGroupResponse.class, description = "comma separated list of affinity groups id that are going to be applied to the virtual machine." - + " Mutually exclusive with affinitygroupnames parameter") - private List affinityGroupIdList; - - @ACL - @Parameter(name = ApiConstants.AFFINITY_GROUP_NAMES, type = CommandType.LIST, collectionType = CommandType.STRING, entityType = AffinityGroupResponse.class, description = "comma separated list of affinity groups names that are going to be applied to the virtual machine." - + "Mutually exclusive with affinitygroupids parameter") - private List affinityGroupNameList; - - @Parameter(name = ApiConstants.DISPLAY_VM, type = CommandType.BOOLEAN, since = "4.2", description = "an optional field, whether to the display the vm to the end user or not.", authorized = {RoleType.Admin}) - private Boolean displayVm; - - @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, since = "4.3", description = "used to specify the custom parameters. 'extraconfig' is not allowed to be passed in details") - private Map details; - - @Parameter(name = ApiConstants.DEPLOYMENT_PLANNER, type = CommandType.STRING, description = "Deployment planner to use for vm allocation. Available to ROOT admin only", since = "4.4", authorized = { RoleType.Admin }) - private String deploymentPlanner; - - @Parameter(name = ApiConstants.DHCP_OPTIONS_NETWORK_LIST, type = CommandType.MAP, description = "DHCP options which are passed to the VM on start up" - + " Example: dhcpoptionsnetworklist[0].dhcp:114=url&dhcpoptionsetworklist[0].networkid=networkid&dhcpoptionsetworklist[0].dhcp:66=www.test.com") - private Map dhcpOptionsNetworkList; - - @Parameter(name = ApiConstants.DATADISK_OFFERING_LIST, type = CommandType.MAP, since = "4.11", description = "datadisk template to disk-offering mapping;" + - " an optional parameter used to create additional data disks from datadisk templates; can't be specified with diskOfferingId parameter") - private Map dataDiskTemplateToDiskOfferingList; - - @Parameter(name = ApiConstants.EXTRA_CONFIG, type = CommandType.STRING, since = "4.12", description = "an optional URL encoded string that can be passed to the virtual machine upon successful deployment", length = 5120) - private String extraConfig; - - @Parameter(name = ApiConstants.COPY_IMAGE_TAGS, type = CommandType.BOOLEAN, since = "4.13", description = "if true the image tags (if any) will be copied to the VM, default value is false") - private Boolean copyImageTags; - - @Parameter(name = ApiConstants.PROPERTIES, type = CommandType.MAP, since = "4.15", - description = "used to specify the vApp properties.") - @LogLevel(LogLevel.Log4jLevel.Off) - private Map vAppProperties; - - @Parameter(name = ApiConstants.NIC_NETWORK_LIST, type = CommandType.MAP, since = "4.15", - description = "VMware only: used to specify network mapping of a vApp VMware template registered \"as-is\"." + - " Example nicnetworklist[0].ip=Nic-101&nicnetworklist[0].network=uuid") - @LogLevel(LogLevel.Log4jLevel.Off) - private Map vAppNetworks; - - @Parameter(name = ApiConstants.DYNAMIC_SCALING_ENABLED, type = CommandType.BOOLEAN, since = "4.16", - description = "true if virtual machine needs to be dynamically scalable") - protected Boolean dynamicScalingEnabled; - - @Parameter(name = ApiConstants.OVERRIDE_DISK_OFFERING_ID, type = CommandType.UUID, since = "4.17", entityType = DiskOfferingResponse.class, description = "the ID of the disk offering for the virtual machine to be used for root volume instead of the disk offering mapped in service offering." + - "In case of virtual machine deploying from ISO, then the diskofferingid specified for root volume is ignored and uses this override disk offering id") - private Long overrideDiskOfferingId; - - @Parameter(name = ApiConstants.IOTHREADS_ENABLED, type = CommandType.BOOLEAN, required = false, - description = "IOThreads are dedicated event loop threads for supported disk devices to perform block I/O requests in order to improve scalability especially on an SMP host/guest with many LUNs.") - private Boolean iothreadsEnabled; - - @Parameter(name = ApiConstants.IO_DRIVER_POLICY, type = CommandType.STRING, description = "Controls specific policies on IO") - private String ioDriverPolicy; - - @Parameter(name = ApiConstants.NIC_MULTIQUEUE_NUMBER, type = CommandType.INTEGER, since = "4.18", - description = "The number of queues for multiqueue NICs.") - private Integer nicMultiqueueNumber; - - @Parameter(name = ApiConstants.NIC_PACKED_VIRTQUEUES_ENABLED, type = CommandType.BOOLEAN, since = "4.18", - description = "Enable packed virtqueues or not.") - private Boolean nicPackedVirtQueues; + @Parameter(name = ApiConstants.SNAPSHOT_ID, type = CommandType.UUID, entityType = SnapshotResponse.class, since = "4.21") + private Long snapshotId; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// - public String getAccountName() { - if (accountName == null) { - return CallContext.current().getCallingAccount().getAccountName(); - } - return accountName; - } - - public Long getDiskOfferingId() { - return diskOfferingId; - } - - public String getDeploymentPlanner() { - return deploymentPlanner; - } - - public String getDisplayName() { - return displayName; - } - - public Long getDomainId() { - if (domainId == null) { - return CallContext.current().getCallingAccount().getDomainId(); - } - return domainId; - } - - public ApiConstants.BootType getBootType() { - if (StringUtils.isNotBlank(bootType)) { - try { - String type = bootType.trim().toUpperCase(); - return ApiConstants.BootType.valueOf(type); - } catch (IllegalArgumentException e) { - String errMesg = "Invalid bootType " + bootType + "Specified for vm " + getName() - + " Valid values are: " + Arrays.toString(ApiConstants.BootType.values()); - logger.warn(errMesg); - throw new InvalidParameterValueException(errMesg); - } - } - return null; - } - - public Map getDetails() { - Map customparameterMap = convertDetailsToMap(details); - - if (getBootType() != null) { - customparameterMap.put(getBootType().toString(), getBootMode().toString()); - } - - if (rootdisksize != null && !customparameterMap.containsKey(VmDetailConstants.ROOT_DISK_SIZE)) { - customparameterMap.put(VmDetailConstants.ROOT_DISK_SIZE, rootdisksize.toString()); - } - - IoDriverPolicy ioPolicy = getIoDriverPolicy(); - if (ioPolicy != null) { - customparameterMap.put(VmDetailConstants.IO_POLICY, ioPolicy.toString()); - } - - if (BooleanUtils.toBoolean(iothreadsEnabled)) { - customparameterMap.put(VmDetailConstants.IOTHREADS, BooleanUtils.toStringTrueFalse(iothreadsEnabled)); - } - - if (nicMultiqueueNumber != null) { - customparameterMap.put(VmDetailConstants.NIC_MULTIQUEUE_NUMBER, nicMultiqueueNumber.toString()); - } - - if (BooleanUtils.toBoolean(nicPackedVirtQueues)) { - customparameterMap.put(VmDetailConstants.NIC_PACKED_VIRTQUEUES_ENABLED, BooleanUtils.toStringTrueFalse(nicPackedVirtQueues)); - } - - return customparameterMap; - } - - - public ApiConstants.BootMode getBootMode() { - if (StringUtils.isNotBlank(bootMode)) { - try { - String mode = bootMode.trim().toUpperCase(); - return ApiConstants.BootMode.valueOf(mode); - } catch (IllegalArgumentException e) { - String msg = String.format("Invalid %s: %s specified for VM: %s. Valid values are: %s", - ApiConstants.BOOT_MODE, bootMode, getName(), Arrays.toString(ApiConstants.BootMode.values())); - logger.error(msg); - throw new InvalidParameterValueException(msg); - } - } - if (ApiConstants.BootType.UEFI.equals(getBootType())) { - String msg = String.format("%s must be specified for the VM with boot type: %s. Valid values are: %s", - ApiConstants.BOOT_MODE, getBootType(), Arrays.toString(ApiConstants.BootMode.values())); - logger.error(msg); - throw new InvalidParameterValueException(msg); - } - return null; - } - - public Map getVmProperties() { - Map map = new HashMap<>(); - if (MapUtils.isNotEmpty(vAppProperties)) { - Collection parameterCollection = vAppProperties.values(); - Iterator iterator = parameterCollection.iterator(); - while (iterator.hasNext()) { - HashMap entry = (HashMap)iterator.next(); - map.put(entry.get("key"), entry.get("value")); - } - } - return map; - } - - public Map getVmNetworkMap() { - Map map = new HashMap<>(); - if (MapUtils.isNotEmpty(vAppNetworks)) { - Collection parameterCollection = vAppNetworks.values(); - Iterator iterator = parameterCollection.iterator(); - while (iterator.hasNext()) { - HashMap entry = (HashMap) iterator.next(); - Integer nic; - try { - nic = Integer.valueOf(entry.get(VmDetailConstants.NIC)); - } catch (NumberFormatException nfe) { - nic = null; - } - String networkUuid = entry.get(VmDetailConstants.NETWORK); - if (logger.isTraceEnabled()) { - logger.trace(String.format("nic, '%s', goes on net, '%s'", nic, networkUuid)); - } - if (nic == null || StringUtils.isEmpty(networkUuid) || _entityMgr.findByUuid(Network.class, networkUuid) == null) { - throw new InvalidParameterValueException(String.format("Network ID: %s for NIC ID: %s is invalid", networkUuid, nic)); - } - map.put(nic, _entityMgr.findByUuid(Network.class, networkUuid).getId()); - } - } - return map; - } - - public String getGroup() { - return group; - } - - public HypervisorType getHypervisor() { - return HypervisorType.getType(hypervisor); - } - - public Boolean isDisplayVm() { - return displayVm; - } - - @Override - public boolean isDisplay() { - if(displayVm == null) - return true; - else - return displayVm; - } - - public List getSecurityGroupNameList() { - return securityGroupNameList; - } - - public List getSecurityGroupIdList() { - return securityGroupIdList; - } - public Long getServiceOfferingId() { return serviceOfferingId; } - public Long getSize() { - return size; - } - public Long getTemplateId() { return templateId; } - public String getUserData() { - return userData; - } - - public Long getUserdataId() { - return userdataId; - } - - public Map getUserdataDetails() { - return convertDetailsToMap(userdataDetails); - } - - public Long getZoneId() { - return zoneId; - } - - public String getPassword() { - return password; - } - - public List getNetworkIds() { - if (MapUtils.isNotEmpty(vAppNetworks)) { - if (CollectionUtils.isNotEmpty(networkIds) || ipAddress != null || getIp6Address() != null || MapUtils.isNotEmpty(ipToNetworkList)) { - throw new InvalidParameterValueException(String.format("%s can't be specified along with %s, %s, %s", ApiConstants.NIC_NETWORK_LIST, ApiConstants.NETWORK_IDS, ApiConstants.IP_ADDRESS, ApiConstants.IP_NETWORK_LIST)); - } else { - return new ArrayList<>(); - } - } - if (ipToNetworkList != null && !ipToNetworkList.isEmpty()) { - if ((networkIds != null && !networkIds.isEmpty()) || ipAddress != null || getIp6Address() != null) { - throw new InvalidParameterValueException("ipToNetworkMap can't be specified along with networkIds or ipAddress"); - } else { - List networks = new ArrayList(); - networks.addAll(getIpToNetworkMap().keySet()); - return networks; - } - } - return networkIds; - } - - public String getName() { - return name; - } - - public List getSSHKeyPairNames() { - List sshKeyPairs = new ArrayList(); - if(sshKeyPairNames != null) { - sshKeyPairs = sshKeyPairNames; - } - if(sshKeyPairName != null && !sshKeyPairName.isEmpty()) { - sshKeyPairs.add(sshKeyPairName); - } - return sshKeyPairs; - } - - public Long getHostId() { - return hostId; - } - - public boolean getStartVm() { - return startVm == null ? true : startVm; - } - - public Map getIpToNetworkMap() { - if ((networkIds != null || ipAddress != null || getIp6Address() != null) && ipToNetworkList != null) { - throw new InvalidParameterValueException("NetworkIds and ipAddress can't be specified along with ipToNetworkMap parameter"); - } - LinkedHashMap ipToNetworkMap = null; - if (ipToNetworkList != null && !ipToNetworkList.isEmpty()) { - ipToNetworkMap = new LinkedHashMap(); - Collection ipsCollection = ipToNetworkList.values(); - Iterator iter = ipsCollection.iterator(); - while (iter.hasNext()) { - HashMap ips = (HashMap)iter.next(); - Long networkId = getNetworkIdFomIpMap(ips); - IpAddresses addrs = getIpAddressesFromIpMap(ips); - ipToNetworkMap.put(networkId, addrs); - } - } - - return ipToNetworkMap; - } - - @Nonnull - private IpAddresses getIpAddressesFromIpMap(HashMap ips) { - String requestedIp = ips.get("ip"); - String requestedIpv6 = ips.get("ipv6"); - String requestedMac = ips.get("mac"); - if (requestedIpv6 != null) { - requestedIpv6 = NetUtils.standardizeIp6Address(requestedIpv6); - } - if (requestedMac != null) { - if(!NetUtils.isValidMac(requestedMac)) { - throw new InvalidParameterValueException("Mac address is not valid: " + requestedMac); - } else if(!NetUtils.isUnicastMac(requestedMac)) { - throw new InvalidParameterValueException("Mac address is not unicast: " + requestedMac); - } - requestedMac = NetUtils.standardizeMacAddress(requestedMac); - } - return new IpAddresses(requestedIp, requestedIpv6, requestedMac); - } - - @Nonnull - private Long getNetworkIdFomIpMap(HashMap ips) { - Long networkId; - final String networkid = ips.get("networkid"); - Network network = _networkService.getNetwork(networkid); - if (network != null) { - networkId = network.getId(); - } else { - try { - networkId = Long.parseLong(networkid); - } catch (NumberFormatException e) { - throw new InvalidParameterValueException("Unable to translate and find entity with networkId: " + networkid); - } - } - return networkId; - } - - public String getIpAddress() { - return ipAddress; - } - - public String getIp6Address() { - if (ip6Address == null) { - return null; - } - return NetUtils.standardizeIp6Address(ip6Address); - } - - - public String getMacAddress() { - if (macAddress == null) { - return null; - } - if(!NetUtils.isValidMac(macAddress)) { - throw new InvalidParameterValueException("Mac address is not valid: " + macAddress); - } else if(!NetUtils.isUnicastMac(macAddress)) { - throw new InvalidParameterValueException("Mac address is not unicast: " + macAddress); - } - return NetUtils.standardizeMacAddress(macAddress); - } - - public List getAffinityGroupIdList() { - if (affinityGroupNameList != null && affinityGroupIdList != null) { - throw new InvalidParameterValueException("affinitygroupids parameter is mutually exclusive with affinitygroupnames parameter"); - } - - // transform group names to ids here - if (affinityGroupNameList != null) { - List affinityGroupIds = new ArrayList(); - for (String groupName : affinityGroupNameList) { - Long groupId = _responseGenerator.getAffinityGroupId(groupName, getEntityOwnerId()); - if (groupId == null) { - throw new InvalidParameterValueException("Unable to find affinity group by name " + groupName); - } else { - affinityGroupIds.add(groupId); - } - } - return affinityGroupIds; - } else { - return affinityGroupIdList; - } - } - - public String getKeyboard() { - // TODO Auto-generated method stub - return keyboard; - } - - public Map> getDhcpOptionsMap() { - Map> dhcpOptionsMap = new HashMap<>(); - if (dhcpOptionsNetworkList != null && !dhcpOptionsNetworkList.isEmpty()) { - - Collection> paramsCollection = this.dhcpOptionsNetworkList.values(); - for (Map dhcpNetworkOptions : paramsCollection) { - String networkId = dhcpNetworkOptions.get(ApiConstants.NETWORK_ID); - - if (networkId == null) { - throw new IllegalArgumentException("No networkid specified when providing extra dhcp options."); - } - - Map dhcpOptionsForNetwork = new HashMap<>(); - dhcpOptionsMap.put(networkId, dhcpOptionsForNetwork); - - for (String key : dhcpNetworkOptions.keySet()) { - if (key.startsWith(ApiConstants.DHCP_PREFIX)) { - int dhcpOptionValue = Integer.parseInt(key.replaceFirst(ApiConstants.DHCP_PREFIX, "")); - dhcpOptionsForNetwork.put(dhcpOptionValue, dhcpNetworkOptions.get(key)); - } else if (!key.equals(ApiConstants.NETWORK_ID)) { - Dhcp.DhcpOptionCode dhcpOptionEnum = Dhcp.DhcpOptionCode.valueOfString(key); - dhcpOptionsForNetwork.put(dhcpOptionEnum.getCode(), dhcpNetworkOptions.get(key)); - } - } - - } - } - - return dhcpOptionsMap; - } - - public Map getDataDiskTemplateToDiskOfferingMap() { - if (diskOfferingId != null && dataDiskTemplateToDiskOfferingList != null) { - throw new InvalidParameterValueException("diskofferingid parameter can't be specified along with datadisktemplatetodiskofferinglist parameter"); - } - if (MapUtils.isEmpty(dataDiskTemplateToDiskOfferingList)) { - return new HashMap(); - } - - HashMap dataDiskTemplateToDiskOfferingMap = new HashMap(); - for (Object objDataDiskTemplates : dataDiskTemplateToDiskOfferingList.values()) { - HashMap dataDiskTemplates = (HashMap) objDataDiskTemplates; - Long dataDiskTemplateId; - DiskOffering dataDiskOffering = null; - VirtualMachineTemplate dataDiskTemplate= _entityMgr.findByUuid(VirtualMachineTemplate.class, dataDiskTemplates.get("datadisktemplateid")); - if (dataDiskTemplate == null) { - dataDiskTemplate = _entityMgr.findById(VirtualMachineTemplate.class, dataDiskTemplates.get("datadisktemplateid")); - if (dataDiskTemplate == null) - throw new InvalidParameterValueException("Unable to translate and find entity with datadisktemplateid " + dataDiskTemplates.get("datadisktemplateid")); - } - dataDiskTemplateId = dataDiskTemplate.getId(); - dataDiskOffering = _entityMgr.findByUuid(DiskOffering.class, dataDiskTemplates.get("diskofferingid")); - if (dataDiskOffering == null) { - dataDiskOffering = _entityMgr.findById(DiskOffering.class, dataDiskTemplates.get("diskofferingid")); - if (dataDiskOffering == null) - throw new InvalidParameterValueException("Unable to translate and find entity with diskofferingId " + dataDiskTemplates.get("diskofferingid")); - } - dataDiskTemplateToDiskOfferingMap.put(dataDiskTemplateId, dataDiskOffering); - } - return dataDiskTemplateToDiskOfferingMap; - } - - public String getExtraConfig() { - return extraConfig; - } - - public boolean getCopyImageTags() { - return copyImageTags == null ? false : copyImageTags; - } - - public Boolean getBootIntoSetup() { - return bootIntoSetup; - } - - public boolean isDynamicScalingEnabled() { - return dynamicScalingEnabled == null ? true : dynamicScalingEnabled; - } - - public Long getOverrideDiskOfferingId() { - return overrideDiskOfferingId; - } - - public ApiConstants.IoDriverPolicy getIoDriverPolicy() { - if (StringUtils.isNotBlank(ioDriverPolicy)) { - try { - String policyType = ioDriverPolicy.trim().toUpperCase(); - return ApiConstants.IoDriverPolicy.valueOf(policyType); - } catch (IllegalArgumentException e) { - String errMesg = String.format("Invalid io policy %s specified for vm %s. Valid values are: %s", ioDriverPolicy, getName(), Arrays.toString(ApiConstants.IoDriverPolicy.values())); - logger.warn(errMesg); - throw new InvalidParameterValueException(errMesg); - } - } - return null; - } - ///////////////////////////////////////////////////// - /////////////// API Implementation/////////////////// - ///////////////////////////////////////////////////// - - @Override - public String getCommandName() { - return s_name; - } - - public static String getResultObjectName() { - return "virtualmachine"; - } - - @Override - public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); - if (accountId == null) { - return CallContext.current().getCallingAccount().getId(); - } - - return accountId; + public Long getVolumeId() { + return volumeId; } - @Override - public String getEventType() { - return EventTypes.EVENT_VM_CREATE; - } - - @Override - public String getCreateEventType() { - return EventTypes.EVENT_VM_CREATE; + public Long getSnapshotId() { + return snapshotId; } - @Override - public String getCreateEventDescription() { - return "creating Vm"; - } - - @Override - public String getEventDescription() { - if(getStartVm()) { - return "starting Vm. Vm Id: " + getEntityUuid(); - } - return "deploying Vm. Vm Id: " + getEntityUuid(); - } - - @Override - public ApiCommandResourceType getApiResourceType() { - return ApiCommandResourceType.VirtualMachine; + public boolean isVolumeOrSnapshotProvided() { + return volumeId != null || snapshotId != null; } @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); @@ -790,12 +112,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()); } @@ -804,13 +126,16 @@ 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) { + throw new CloudRuntimeException("Please provide only one of the following parameters - template ID, volume ID or snapshot ID"); + } + try { UserVm vm = _userVmService.createVirtualMachine(this); @@ -818,7 +143,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 aa121162cb4e..9e2f2bcb72ce 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; @@ -116,7 +116,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,7 +131,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)); UserVm result = _userVmService.destroyVm(this); UserVmResponse response = new UserVmResponse(); @@ -140,10 +140,11 @@ public void execute() throws ResourceUnavailableException, ConcurrentOperationEx if (responses != null && !responses.isEmpty()) { response = responses.get(0); } - response.setResponseName("virtualmachine"); + response.setResponseName(getCommandName()); + response.setObjectName("virtualmachine"); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to destroy vm"); + 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 ce6114c7fd85..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 @@ -16,9 +16,6 @@ // under the License. package org.apache.cloudstack.api.command.user.vm; -import java.security.InvalidParameterException; - - import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.ACL; import org.apache.cloudstack.api.APICommand; @@ -28,11 +25,12 @@ import org.apache.cloudstack.api.response.GetVMPasswordResponse; import org.apache.cloudstack.api.response.UserVmResponse; +import com.cloud.exception.InvalidParameterValueException; import com.cloud.user.Account; 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 { @@ -42,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; ///////////////////////////////////////////////////// @@ -61,7 +59,7 @@ public Long getId() { public void execute() { String passwd = _mgr.getVMPassword(this); if (passwd == null || passwd.equals("")) - throw new InvalidParameterException("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..be94315abe76 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 @@ -31,7 +31,7 @@ import javax.inject.Inject; -@APICommand(name = "listVMSchedule", description = "List VM Schedules.", responseObject = VMScheduleResponse.class, +@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 { @@ -42,14 +42,14 @@ public class ListVMScheduleCmd extends BaseListCmd { 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,7 +61,7 @@ public class ListVMScheduleCmd extends BaseListCmd { @Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN, required = false, - description = "ID of VM schedule") + description = "ID of Instance schedule") private Boolean enabled; ///////////////////////////////////////////////////// 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 2d1160fb7a75..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 @@ -18,7 +18,13 @@ import java.util.ArrayList; import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.affinity.AffinityGroupResponse; @@ -32,6 +38,7 @@ import org.apache.cloudstack.api.command.user.UserCmd; import org.apache.cloudstack.api.response.AutoScaleVmGroupResponse; import org.apache.cloudstack.api.response.BackupOfferingResponse; +import org.apache.cloudstack.api.response.ExtensionResponse; import org.apache.cloudstack.api.response.InstanceGroupResponse; import org.apache.cloudstack.api.response.IsoVmResponse; import org.apache.cloudstack.api.response.ListResponse; @@ -40,113 +47,142 @@ import org.apache.cloudstack.api.response.SecurityGroupResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; import org.apache.cloudstack.api.response.TemplateResponse; +import org.apache.cloudstack.api.response.UserDataResponse; import org.apache.cloudstack.api.response.UserResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.api.response.VpcResponse; import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import com.cloud.cpu.CPU; import com.cloud.exception.InvalidParameterValueException; import com.cloud.server.ResourceIcon; import com.cloud.server.ResourceTag; +import com.cloud.storage.GuestOS; import com.cloud.vm.VirtualMachine; -@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 { - private static final String s_name = "listvirtualmachinesresponse"; ///////////////////////////////////////////////////// //////////////// 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]." - + " If no parameter is passed in, the details will be defaulted to all") + + " 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") + private Long userdataId; + + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + description = "CPU arch of the VM", + since = "4.20.1") + private String arch; + + @Parameter(name = ApiConstants.LEASED, type = CommandType.BOOLEAN, + description = "Whether to return only leased instances", + since = "4.21.0") + private Boolean onlyLeasedInstances = false; + + @Parameter(name = ApiConstants.GPU_ENABLED, + type = CommandType.BOOLEAN, + description = "Flag to indicate if the VMs should be filtered by GPU support. If set to true, only VMs that support GPU will be returned.", + since = "4.21.0") + private Boolean gpuEnabled; + + @Parameter(name = ApiConstants.EXTENSION_ID, type = CommandType.UUID, + entityType = ExtensionResponse.class, description = "The ID of the Orchestrator extension for the VM", + since = "4.21.0") + private Long extensionId; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -237,22 +273,36 @@ public Long getAutoScaleVmGroupId() { return autoScaleVmGroupId; } + protected boolean isViewDetailsEmpty() { + return CollectionUtils.isEmpty(viewDetails); + } + + public Long getUserdataId() { + return userdataId; + } + public EnumSet getDetails() throws InvalidParameterValueException { - EnumSet dv; - if (viewDetails == null || viewDetails.size() <= 0) { - dv = EnumSet.of(VMDetails.all); - } else { - try { - ArrayList dc = new ArrayList(); - for (String detail : viewDetails) { - dc.add(VMDetails.valueOf(detail)); - } - dv = EnumSet.copyOf(dc); - } catch (IllegalArgumentException e) { - throw new InvalidParameterValueException("The details parameter contains a non permitted value. The allowed values are " + EnumSet.allOf(VMDetails.class)); + if (isViewDetailsEmpty()) { + if (_queryService.ReturnVmStatsOnVmList.value()) { + return EnumSet.of(VMDetails.all); } + + Set allDetails = new HashSet<>(Set.of(VMDetails.values())); + allDetails.remove(VMDetails.stats); + allDetails.remove(VMDetails.all); + return EnumSet.copyOf(allDetails); + } + + try { + Set dc = new HashSet<>(); + for (String detail : viewDetails) { + dc.add(VMDetails.valueOf(detail)); + } + + return EnumSet.copyOf(dc); + } catch (IllegalArgumentException e) { + throw new InvalidParameterValueException("The details parameter contains a non permitted value. The allowed values are " + EnumSet.allOf(VMDetails.class)); } - return dv; } @Override @@ -272,13 +322,25 @@ public Boolean getVnf() { return isVnf; } + public CPU.CPUArch getArch() { + return StringUtils.isBlank(arch) ? null : CPU.CPUArch.fromType(arch); + } + + public boolean getOnlyLeasedInstances() { + return BooleanUtils.toBoolean(onlyLeasedInstances); + } + + public Boolean getGpuEnabled() { + return gpuEnabled; + } + + public Long getExtensionId() { + return extensionId; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// - @Override - public String getCommandName() { - return s_name; - } @Override public ApiCommandResourceType getApiResourceType() { @@ -295,22 +357,75 @@ public void execute() { setResponseObject(response); } - protected void updateVMResponse(List response) { - for (UserVmResponse vmResponse : response) { - ResourceIcon resourceIcon = resourceIconManager.getByResourceTypeAndUuid(ResourceTag.ResourceObjectType.UserVm, vmResponse.getId()); - if (resourceIcon == null) { - ResourceTag.ResourceObjectType type = ResourceTag.ResourceObjectType.Template; - String uuid = vmResponse.getTemplateId(); - if (vmResponse.getIsoId() != null) { - uuid = vmResponse.getIsoId(); - type = ResourceTag.ResourceObjectType.ISO; - } - resourceIcon = resourceIconManager.getByResourceTypeAndUuid(type, uuid); - if (resourceIcon == null) { - continue; - } + protected Map getResourceIconsUsingOsCategory(List responses) { + Set guestOsIds = responses.stream().map(UserVmResponse::getGuestOsId).collect(Collectors.toSet()); + List guestOSList = _entityMgr.listByUuids(GuestOS.class, guestOsIds); + Map guestOSMap = guestOSList.stream() + .collect(Collectors.toMap(GuestOS::getUuid, Function.identity())); + Set guestOsCategoryIds = guestOSMap.values().stream() + .map(GuestOS::getCategoryId) + .collect(Collectors.toSet()); + Map guestOsCategoryIcons = + resourceIconManager.getByResourceTypeAndIds(ResourceTag.ResourceObjectType.GuestOsCategory, + guestOsCategoryIds); + Map vmIcons = new HashMap<>(); + for (UserVmResponse response : responses) { + GuestOS guestOS = guestOSMap.get(response.getGuestOsId()); + if (guestOS != null) { + vmIcons.put(response.getId(), guestOsCategoryIcons.get(guestOS.getCategoryId())); + } + } + return vmIcons; + } + + protected Map getResourceIconsForUsingTemplateIso(List responses) { + Map vmTemplateIsoIdMap = new HashMap<>(); + Set templateUuids = new HashSet<>(); + Set isoUuids = new HashSet<>(); + for (UserVmResponse vmResponse : responses) { + if (vmResponse.getTemplateId() != null) { + templateUuids.add(vmResponse.getTemplateId()); + vmTemplateIsoIdMap.put(vmResponse.getId(), vmResponse.getTemplateId()); + } + if (vmResponse.getIsoId() != null) { + isoUuids.add(vmResponse.getIsoId()); + vmTemplateIsoIdMap.put(vmResponse.getId(), vmResponse.getIsoId()); + } + } + Map templateOrIsoIcons = resourceIconManager.getByResourceTypeAndUuids(ResourceTag.ResourceObjectType.Template, templateUuids); + templateOrIsoIcons.putAll(resourceIconManager.getByResourceTypeAndUuids(ResourceTag.ResourceObjectType.ISO, isoUuids)); + Map vmIcons = new HashMap<>(); + List noTemplateIsoIconResponses = new ArrayList<>(); + for (UserVmResponse response : responses) { + String uuid = vmTemplateIsoIdMap.get(response.getId()); + if (StringUtils.isNotBlank(uuid) && templateOrIsoIcons.containsKey(uuid)) { + vmIcons.put(response.getId(), + templateOrIsoIcons.get(vmTemplateIsoIdMap.get(response.getId()))); + continue; + } + noTemplateIsoIconResponses.add(response); + } + vmIcons.putAll(getResourceIconsUsingOsCategory(noTemplateIsoIconResponses)); + return vmIcons; + } + + protected void updateVMResponse(List responses) { + if (CollectionUtils.isEmpty(responses)) { + return; + } + Set vmUuids = responses.stream().map(UserVmResponse::getId).collect(Collectors.toSet()); + Map vmIcons = resourceIconManager.getByResourceTypeAndUuids(ResourceTag.ResourceObjectType.UserVm, vmUuids); + List noVmIconResponses = responses + .stream() + .filter(r -> !vmIcons.containsKey(r.getId())) + .collect(Collectors.toList()); + vmIcons.putAll(getResourceIconsForUsingTemplateIso(noVmIconResponses)); + for (UserVmResponse vmResponse : responses) { + ResourceIcon icon = vmIcons.get(vmResponse.getId()); + if (icon == null) { + continue; } - ResourceIconResponse iconResponse = _responseGenerator.createResourceIconResponse(resourceIcon); + ResourceIconResponse iconResponse = _responseGenerator.createResourceIconResponse(icon); vmResponse.setResourceIconResponse(iconResponse); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVnfAppliancesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVnfAppliancesCmd.java new file mode 100644 index 000000000000..e7bbb97d78c3 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVnfAppliancesCmd.java @@ -0,0 +1,39 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.vm; + +import com.cloud.vm.VirtualMachine; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ResponseObject.ResponseView; +import org.apache.cloudstack.api.command.user.UserCmd; + +import org.apache.cloudstack.api.response.UserVmResponse; + +@APICommand(name = "listVnfAppliances", description = "List VNF appliance owned by the account.", + responseObject = UserVmResponse.class, + responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}, + since = "4.19.1") +public class ListVnfAppliancesCmd extends ListVMsCmd implements UserCmd { + + @Override + public Boolean getVnf() { + return true; + } +} 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 10900f61b22b..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 @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.command.user.vm; +import com.cloud.exception.ResourceAllocationException; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.ACL; import org.apache.cloudstack.api.APICommand; @@ -38,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"; @@ -48,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") @@ -99,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 @@ -113,8 +114,8 @@ public Long getApiResourceId() { } @Override - public void execute() throws ResourceUnavailableException, InsufficientCapacityException { - CallContext.current().setEventDetails("Vm Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getId())); + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException { + CallContext.current().setEventDetails("Instance ID: " + getResourceUuid(ApiConstants.ID)); UserVm result; result = _userVmService.rebootVirtualMachine(this); if (result !=null){ @@ -122,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 a4cd6159dfc7..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); } ///////////////////////////////////////////////////// @@ -127,12 +127,12 @@ public NetworkType getNetworkType() { private boolean isZoneSGEnabled() { Network ntwk = _entityMgr.findById(Network.class, getNetworkId()); DataCenter dc = _entityMgr.findById(DataCenter.class, ntwk.getDataCenterId()); - return dc.isSecurityGroupEnabled(); + return dc.isSecurityGroupEnabled() || _ntwkModel.isSecurityGroupSupportedForZone(dc.getId()); } @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 089dfaecf946..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,36 +52,36 @@ 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." + + "Using HTTP POST (via POST body), you can send up to 1MB of data after base64 encoding. " + "You also need to change vm.userdata.max.length value", length = 1048576) private String userData; - @Parameter(name = ApiConstants.USER_DATA_ID, type = CommandType.UUID, entityType = UserDataResponse.class, description = "the ID of the userdata") + @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 e1c4dd5f678e..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 @@ -16,9 +16,9 @@ // under the License. package org.apache.cloudstack.api.command.user.vm; -import org.apache.cloudstack.api.ApiCommandResourceType; - +import com.cloud.vm.VmDetailConstants; import org.apache.cloudstack.acl.SecurityChecker.AccessType; +import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ACL; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -28,6 +28,7 @@ import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.DiskOfferingResponse; import org.apache.cloudstack.api.response.TemplateResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.context.CallContext; @@ -41,7 +42,9 @@ import com.cloud.uservm.UserVm; 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.Restricted, entityType = {VirtualMachine.class}, +import java.util.Map; + +@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 { @@ -49,15 +52,37 @@ 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, + type = CommandType.UUID, + entityType = DiskOfferingResponse.class, + description = "Override root volume's diskoffering.", since = "4.19.1") + private Long rootDiskOfferingId; + + @Parameter(name = ApiConstants.ROOT_DISK_SIZE, + type = CommandType.LONG, + description = "Override root volume's size (in GB). Analogous to details[0].rootdisksize, which takes precedence over this parameter if both are provided", + since = "4.19.1") + private Long rootDiskSize; + + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, since = "4.19.1", + description = "used to specify the custom parameters") + private Map details; + + @Parameter(name = ApiConstants.EXPUNGE, + type = CommandType.BOOLEAN, + description = "Optional field to expunge old root volume after restore.", + since = "4.19.1") + private Boolean expungeRootDisk; + @Override public String getEventType() { return EventTypes.EVENT_VM_RESTORE; @@ -65,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()); } } @@ -110,6 +135,22 @@ public Long getId() { return getVmId(); } + public Long getRootDiskOfferingId() { + return rootDiskOfferingId; + } + + public Map getDetails() { + Map customparameterMap = convertDetailsToMap(details); + if (rootDiskSize != null && !customparameterMap.containsKey(VmDetailConstants.ROOT_DISK_SIZE)) { + customparameterMap.put(VmDetailConstants.ROOT_DISK_SIZE, rootDiskSize.toString()); + } + return customparameterMap; + } + + public Boolean getExpungeRootDisk() { + return expungeRootDisk != null && expungeRootDisk; + } + @Override public Long getApiResourceId() { return getId(); 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 8bc4f0ff3b15..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") @@ -102,6 +102,10 @@ public Long getId() { return id; } + public void setId(Long id) { + this.id = id; + } + public Long getHostId() { return hostId; } @@ -157,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 @@ -173,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); @@ -183,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 9f72ac17c8f4..e3ad0502f454 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java @@ -16,18 +16,20 @@ // under the License. package org.apache.cloudstack.api.command.user.vm; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import com.cloud.uservm.UserVm; +import com.cloud.utils.StringUtils; import com.cloud.utils.exception.CloudRuntimeException; -import org.apache.cloudstack.api.response.UserDataResponse; - +import com.cloud.utils.net.Dhcp; +import com.cloud.vm.VirtualMachine; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.ACL; import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiArgValidator; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; @@ -38,19 +40,20 @@ import org.apache.cloudstack.api.command.user.UserCmd; import org.apache.cloudstack.api.response.GuestOSResponse; import org.apache.cloudstack.api.response.SecurityGroupResponse; +import org.apache.cloudstack.api.response.UserDataResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.vm.lease.VMLeaseManager; +import org.apache.commons.lang3.EnumUtils; -import com.cloud.exception.InsufficientCapacityException; -import com.cloud.exception.ResourceUnavailableException; -import com.cloud.user.Account; -import com.cloud.uservm.UserVm; -import com.cloud.utils.net.Dhcp; -import com.cloud.vm.VirtualMachine; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; -@APICommand(name = "updateVirtualMachine", description="Updates properties of a virtual machine. The VM has to be stopped and restarted for the " + - "new properties to take effect. UpdateVirtualMachine does not first check whether the VM is stopped. " + - "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"; @@ -59,55 +62,55 @@ 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." + + "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, 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", 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.") @@ -118,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 @@ -126,29 +129,54 @@ 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", authorized = { RoleType.Admin }, 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, + type = CommandType.BOOLEAN, since = "4.20.0", + description = "Set delete protection for the virtual machine. If " + + "true, the instance will be protected from deletion. " + + "Note: If the instance is managed by another service like" + + " autoscaling groups or CKS, delete protection will be ignored.") + private Boolean deleteProtection; + + @Parameter(name = ApiConstants.INSTANCE_LEASE_DURATION, type = CommandType.INTEGER, since = "4.21.0", + description = "Number of days to lease the instance from now onward. Use -1 to remove the existing lease") + private Integer leaseDuration; + + @Parameter(name = ApiConstants.INSTANCE_LEASE_EXPIRY_ACTION, type = CommandType.STRING, since = "4.21.0", + description = "Lease expiry action, valid values are STOP and DESTROY") + private String leaseExpiryAction; + + @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; } @@ -213,6 +241,10 @@ public boolean isCleanupDetails(){ return cleanupDetails == null ? false : cleanupDetails.booleanValue(); } + public Boolean getDeleteProtection() { + return deleteProtection; + } + public Map> getDhcpOptionsMap() { Map> dhcpOptionsMap = new HashMap<>(); if (dhcpOptionsNetworkList != null && !dhcpOptionsNetworkList.isEmpty()) { @@ -222,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<>(); @@ -248,14 +280,18 @@ public String getExtraConfig() { return extraConfig; } - ///////////////////////////////////////////////////// - /////////////// API Implementation/////////////////// - ///////////////////////////////////////////////////// - public Long getOsTypeId() { return osTypeId; } + public boolean isCleanupExtraConfig() { + return Boolean.TRUE.equals(cleanupExtraConfig); + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + @Override public String getCommandName() { return s_name; @@ -277,19 +313,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"); } } @@ -310,4 +346,21 @@ public Long getApiResourceId() { public ApiCommandResourceType getApiResourceType() { return ApiCommandResourceType.VirtualMachine; } + + public Integer getLeaseDuration() { + return leaseDuration; + } + + public VMLeaseManager.ExpiryAction getLeaseExpiryAction() { + if (StringUtils.isBlank(leaseExpiryAction)) { + return null; + } + VMLeaseManager.ExpiryAction action = EnumUtils.getEnumIgnoreCase(VMLeaseManager.ExpiryAction.class, leaseExpiryAction); + if (action == null) { + throw new InvalidParameterValueException("Invalid value configured for leaseexpiryaction, valid values are: " + + com.cloud.utils.EnumUtils.listValues(VMLeaseManager.ExpiryAction.values())); + } + return action; + } + } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMScheduleCmd.java index 72f85c1a99ce..b7222944fe07 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 @@ -32,7 +32,7 @@ import javax.inject.Inject; import java.util.Date; -@APICommand(name = "updateVMSchedule", description = "Update VM Schedule.", responseObject = VMScheduleResponse.class, +@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 { @@ -43,7 +43,7 @@ public class UpdateVMScheduleCmd extends BaseCmd { 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 +55,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,21 +67,21 @@ public class UpdateVMScheduleCmd extends BaseCmd { @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, required = false, - description = "start date from which the schedule becomes active" + description = "Start date from which the schedule becomes active" + "Use 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" + description = "End date after which the schedule becomes inactive" + "Use 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; ///////////////////////////////////////////////////// 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 41f480d60876..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()); } } @@ -136,4 +135,10 @@ public long getEntityOwnerId() { public ApiCommandResourceType getApiResourceType() { return ApiCommandResourceType.VmSnapshot; } + + @Override + public Long getApiResourceId() { + return getEntityId(); + } + } 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..f39853512281 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, @@ -81,7 +81,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 new file mode 100644 index 000000000000..fdbd4a61c072 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/CheckAndRepairVolumeCmd.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 +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.volume; + +import java.util.Arrays; + +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.ResponseObject.ResponseView; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.VolumeResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.commons.lang3.EnumUtils; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.storage.Volume; +import com.cloud.user.Account; +import com.cloud.utils.Pair; +import com.cloud.utils.StringUtils; + +@APICommand(name = "checkVolume", description = "Check the volume for any errors or leaks and also repairs when repair parameter is passed, this is currently supported for KVM only", responseObject = VolumeResponse.class, entityType = {Volume.class}, + since = "4.19.1", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class CheckAndRepairVolumeCmd extends BaseAsyncCmd { + + private static final String s_name = "checkandrepairvolumeresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VolumeResponse.class, required = true, description = "The ID of the volume") + private Long id; + + @Parameter(name = ApiConstants.REPAIR, type = CommandType.STRING, required = false, description = "parameter to repair the volume, leaks or all are the possible values") + private String repair; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public enum RepairValues { + LEAKS, ALL + } + + public Long getId() { + return id; + } + + public String getRepair() { + if (org.apache.commons.lang3.StringUtils.isNotEmpty(repair)) { + RepairValues repairType = EnumUtils.getEnumIgnoreCase(RepairValues.class, repair); + if (repairType == null) { + throw new InvalidParameterValueException(String.format("Repair parameter can only take the following values: %s", Arrays.toString(RepairValues.values()))); + } + return repair.toLowerCase(); + } + return null; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + Volume volume = _entityMgr.findById(Volume.class, getId()); + if (volume != null) { + return volume.getAccountId(); + } + + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } + + @Override + public String getEventType() { + return EventTypes.EVENT_VOLUME_CHECK; + } + + @Override + public String getEventDescription() { + return "Starting checking and repairing operation on volume: " + getResourceUuid(ApiConstants.ID); + } + + @Override + public Long getApiResourceId() { + return id; + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.Volume; + } + + @Override + public void execute() throws ResourceAllocationException { + CallContext.current().setEventDetails("Volume ID: " + getResourceUuid(ApiConstants.ID)); + Pair result = _volumeService.checkAndRepairVolume(this); + Volume volume = _responseGenerator.findVolumeById(getId()); + if (result != null) { + VolumeResponse response = _responseGenerator.createVolumeResponse(ResponseView.Full, volume); + response.setVolumeCheckResult(StringUtils.parseJsonToMap(result.first())); + if (getRepair() != null) { + response.setVolumeRepairResult(StringUtils.parseJsonToMap(result.second())); + } + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to check volume and repair"); + } + } +} 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..5bcf3a141178 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 @@ -43,7 +43,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,58 +55,58 @@ 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; ///////////////////////////////////////////////////// @@ -188,7 +188,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 +203,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 +230,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..e102d51f0378 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,7 +84,7 @@ public ApiCommandResourceType getApiResourceType() { @Override public void execute() throws ConcurrentOperationException { - CallContext.current().setEventDetails("Volume Id: " + this._uuidMgr.getUuid(Volume.class, getId())); + CallContext.current().setEventDetails("Volume ID: " + getResourceUuid(ApiConstants.ID)); Volume result = _volumeService.destroyVolume(id, CallContext.current().getCallingAccount(), true, false); if (result != null) { SuccessResponse response = new SuccessResponse(getCommandName()); 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..12a44f76ea15 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,7 +115,7 @@ public Long getApiResourceId() { @Override public void execute() { - CallContext.current().setEventDetails("Volume Id: " + getId()); + CallContext.current().setEventDetails("Volume ID: " + getResourceUuid(ApiConstants.ID)); Volume result = _volumeService.destroyVolume(getId(), CallContext.current().getCallingAccount(), getExpunge(), false); if (result != null) { VolumeResponse response = _responseGenerator.createVolumeResponse(ResponseView.Restricted, result); 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..66a558abf982 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; ///////////////////////////////////////////////////// @@ -126,15 +126,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 1146f80f0e2c..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,7 +16,6 @@ // under the License. package org.apache.cloudstack.api.command.user.volume; - import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.ACL; import org.apache.cloudstack.api.APICommand; @@ -31,9 +30,7 @@ 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.storage.Upload; import com.cloud.storage.Volume; import com.cloud.user.Account; @@ -48,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; ///////////////////////////////////////////////////// @@ -116,28 +113,16 @@ public String getEventType() { @Override public String getEventDescription() { - return "Extraction job"; + return "Extracting volume with ID: " + getResourceUuid(ApiConstants.ID) + " from zone with ID: " + getResourceUuid(ApiConstants.ZONE_ID); } @Override public void execute() { - CallContext.current().setEventDetails("Volume Id: " + this._uuidMgr.getUuid(Volume.class, getId())); + CallContext.current().setEventDetails(getEventDescription()); String uploadUrl = _volumeService.extractVolume(this); if (uploadUrl != null) { - ExtractResponse response = new ExtractResponse(); + ExtractResponse response = _responseGenerator.createVolumeExtractResponse(id, zoneId, getEntityOwnerId(), mode, uploadUrl); response.setResponseName(getCommandName()); - response.setObjectName("volume"); - Volume vol = _entityMgr.findById(Volume.class, id); - response.setId(vol.getUuid()); - response.setName(vol.getName()); - DataCenter zone = _entityMgr.findById(DataCenter.class, zoneId); - response.setZoneId(zone.getUuid()); - response.setZoneName(zone.getName()); - response.setMode(mode); - response.setState(Upload.Status.DOWNLOAD_URL_CREATED.toString()); - Account account = _entityMgr.findById(Account.class, getEntityOwnerId()); - response.setAccountId(account.getUuid()); - response.setUrl(uploadUrl); setResponseObject(response); } else { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to extract volume"); 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 a583675da76f..a4cd299dae9c 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 @@ -31,6 +31,7 @@ import org.apache.cloudstack.api.response.HostResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.PodResponse; +import org.apache.cloudstack.api.response.ServiceOfferingResponse; import org.apache.cloudstack.api.response.StoragePoolResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.api.response.VolumeResponse; @@ -49,51 +50,60 @@ 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.DISK_OFFERING_ID, type = CommandType.UUID, entityType = DiskOfferingResponse.class, description = "list volumes by disk offering", since = "4.4") + @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 " + + "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") 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.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", + authorized = { RoleType.Admin }) + private Boolean encrypted; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -118,6 +128,10 @@ public Long getPodId() { return podId; } + public Long getServiceOfferingId() { + return serviceOfferingId; + } + public Long getDiskOfferingId() { return diskOfferingId; } @@ -151,6 +165,10 @@ public String getState() { return state; } + public Boolean isEncrypted() { + return encrypted; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// 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 9254bad207bf..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,9 +70,13 @@ 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, + description = "Flag to allow automatic migration of the volume to another suitable storage pool that accommodates the new size", since = "4.20.1") + private Boolean autoMigrate; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -101,6 +105,10 @@ public Long getId() { return getEntityId(); } + public void setId(Long id) { + this.id = id; + } + public Long getMinIops() { return minIops; } @@ -113,6 +121,10 @@ public Long getSize() { return size; } + public void setSize(Long size) { + this.size = size; + } + public boolean isShrinkOk() { return shrinkOk; } @@ -121,6 +133,10 @@ public Long getNewDiskOfferingId() { return newDiskOfferingId; } + public boolean getAutoMigrate() { + return autoMigrate == null ? false : autoMigrate; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -174,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 @@ -186,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 467c587cc731..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,12 +71,20 @@ 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, + type = CommandType.BOOLEAN, since = "4.20.0", + description = "Set delete protection for the volume. If true, The volume " + + "will be protected from deletion. Note: If the volume is managed by " + + "another service like autoscaling groups or CKS, delete protection will be " + + "ignored.") + private Boolean deleteProtection; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -109,6 +117,10 @@ public String getName() { return name; } + public Boolean getDeleteProtection() { + return deleteProtection; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -166,9 +178,9 @@ 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(), - getCustomId(), getEntityOwnerId(), getChainInfo(), getName()); + getDeleteProtection(), getCustomId(), getEntityOwnerId(), getChainInfo(), getName()); if (result != null) { VolumeResponse response = _responseGenerator.createVolumeResponse(getResponseView(), result); response.setResponseName(getCommandName()); 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 b28c02cb8004..11930bcbab61 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateStaticRouteCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateStaticRouteCmd.java @@ -27,6 +27,7 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.PrivateGatewayResponse; import org.apache.cloudstack.api.response.StaticRouteResponse; +import org.apache.cloudstack.api.response.VpcResponse; import org.apache.cloudstack.context.CallContext; import com.cloud.event.EventTypes; @@ -46,19 +47,40 @@ public class CreateStaticRouteCmd extends BaseAsyncCreateCmd { type = CommandType.UUID, entityType = PrivateGatewayResponse.class, required = true, - description = "the gateway id we are creating static route for") + description = "The gateway ID we are creating static route for. Mutually exclusive with the nexthop parameter") private Long gatewayId; - @Parameter(name = ApiConstants.CIDR, required = true, type = CommandType.STRING, description = "static route cidr") + @Parameter(name = ApiConstants.VPC_ID, + type = CommandType.UUID, + entityType = VpcResponse.class, + description = "the vpc id for which the static route is created. This is required for nexthop parameter", + since = "4.21.0") + private Long vpcId; + + @Parameter(name = ApiConstants.NEXT_HOP, + type = CommandType.STRING, + description = "the next hop of static route. Mutually exclusive with the gatewayid parameter", + since = "4.21.0") + private String nextHop; + + @Parameter(name = ApiConstants.CIDR, required = true, type = CommandType.STRING, description = "Static route CIDR") private String cidr; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// - public long getGatewayId() { + public Long getGatewayId() { return gatewayId; } + public Long getVpcId() { + return vpcId; + } + + public String getNextHop() { + return nextHop; + } + public String getCidr() { return cidr; } @@ -69,11 +91,11 @@ public String getCidr() { @Override public void create() throws ResourceAllocationException { try { - StaticRoute result = _vpcService.createStaticRoute(getGatewayId(), getCidr()); + StaticRoute result = _vpcService.createStaticRoute(getGatewayId(), getVpcId(), getNextHop(), getCidr()); setEntityId(result.getId()); setEntityUuid(result.getUuid()); } catch (NetworkRuleConflictException ex) { - 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()); } @@ -94,7 +116,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()); @@ -107,18 +129,15 @@ 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"); } } } @Override public long getEntityOwnerId() { - VpcGateway gateway = _entityMgr.findById(VpcGateway.class, gatewayId); - if (gateway == null) { - throw new InvalidParameterValueException("Invalid gateway id is specified"); - } - return _entityMgr.findById(Vpc.class, gateway.getVpcId()).getAccountId(); + Long vpcId = getSyncObjId(); + return _entityMgr.findById(Vpc.class, vpcId).getAccountId(); } @Override @@ -128,11 +147,20 @@ public String getSyncObjType() { @Override public Long getSyncObjId() { - VpcGateway gateway = _entityMgr.findById(VpcGateway.class, gatewayId); - if (gateway == null) { - throw new InvalidParameterValueException("Invalid id is specified for the gateway"); + if (gatewayId != null) { + VpcGateway gateway = _entityMgr.findById(VpcGateway.class, gatewayId); + if (gateway == null) { + throw new InvalidParameterValueException("Invalid id is specified for the gateway"); + } + return gateway.getVpcId(); + } else if (vpcId != null) { + Vpc vpc = _entityMgr.findById(Vpc.class, vpcId); + if (vpc == null) { + throw new InvalidParameterValueException("Invalid vpc id is specified"); + } + return vpc.getId(); } - return gateway.getVpcId(); + throw new InvalidParameterValueException("One of vpcId or gatewayId must be specified"); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java index 94f05f707a0a..2adbbd664085 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.user.vpc; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.apache.cloudstack.acl.RoleType; @@ -51,72 +52,84 @@ 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'.") private String displayText; - @Parameter(name = ApiConstants.CIDR, type = CommandType.STRING, required = true, description = "the cidr of the VPC. All VPC " + - "guest networks' cidrs should be within this CIDR") + @Parameter(name = ApiConstants.CIDR, type = CommandType.STRING, + 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, + description = "the CIDR size of VPC. For regular users, this is required for VPC with ROUTED mode.", + since = "4.20.0") + 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; + @Parameter(name=ApiConstants.AS_NUMBER, type=CommandType.LONG, since = "4.20.0", description="the AS Number of the VPC tiers") + private Long asNumber; + + @Parameter(name=ApiConstants.USE_VIRTUAL_ROUTER_IP_RESOLVER, type=CommandType.BOOLEAN, + description="(optional) for NSX based VPCs: when set to true, use the VR IP as nameserver, otherwise use DNS1 and DNS2") + private Boolean useVrIpResolver; + // /////////////////////////////////////////////////// // ///////////////// Accessors /////////////////////// // /////////////////////////////////////////////////// @@ -141,6 +154,10 @@ public String getCidr() { return cidr; } + public Integer getCidrSize() { + return cidrSize; + } + public String getDisplayText() { return StringUtils.isEmpty(displayText) ? vpcName : displayText; } @@ -189,6 +206,14 @@ public String getSourceNatIP() { return sourceNatIP; } + public Long getAsNumber() { + return asNumber; + } + + public boolean getUseVrIpResolver() { + return BooleanUtils.toBoolean(useVrIpResolver); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -208,11 +233,7 @@ public void create() throws ResourceAllocationException { public void execute() { Vpc vpc = null; try { - if (isStart()) { - _vpcService.startVpc(getEntityId(), true); - } else { - logger.debug("Not starting VPC as " + ApiConstants.START + "=false was passed to the API"); - } + _vpcService.startVpc(this); vpc = _entityMgr.findById(Vpc.class, getEntityId()); } catch (ResourceUnavailableException ex) { logger.warn("Exception: ", ex); @@ -252,7 +273,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..f2327c9073f3 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,25 +47,25 @@ 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; @@ -143,7 +143,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 9e950310cdc2..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,31 +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()); - } - + VpnUsersResponse vpnResponse = _responseGenerator.createVpnUserResponse(vpnUser); vpnResponse.setResponseName(getCommandName()); - vpnResponse.setObjectName("vpnuser"); setResponseObject(vpnResponse); } @@ -144,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 6f31176c4ff6..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 @@ -28,11 +28,12 @@ import org.apache.cloudstack.api.BaseAsyncCreateCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.IPAddressResponse; import org.apache.cloudstack.api.response.Site2SiteVpnGatewayResponse; import org.apache.cloudstack.api.response.VpcResponse; import org.apache.cloudstack.context.CallContext; -@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 { @@ -44,10 +45,17 @@ public class CreateVpnGatewayCmd extends BaseAsyncCreateCmd { type = CommandType.UUID, entityType = VpcResponse.class, required = true, - description = "public ip address id of the vpn gateway") + description = "Public IP address id of the VPN gateway") private Long vpcId; - @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.IP_ADDRESS_ID, + type = CommandType.UUID, + entityType = IPAddressResponse.class, + description = "the public IP address ID for which VPN gateway is being enabled. By default the source NAT IP or router IP will be used.", + since = "4.21.0") + private Long ipAddressId; + + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "An optional field, whether to the display the VPN to the end User or not", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; ///////////////////////////////////////////////////// @@ -58,6 +66,10 @@ public Long getVpcId() { return vpcId; } + public Long getIpAddressId() { + return ipAddressId; + } + @Deprecated public Boolean getDisplay() { return display; @@ -84,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 @@ -94,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 48e7a9ee5193..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 @@ -104,9 +104,9 @@ public String getEventType() { public void execute() { Account owner = _accountService.getAccount(getEntityOwnerId()); long ownerId = owner.getId(); - boolean result = _ravService.removeVpnUser(ownerId, userName, CallContext.current().getCallingAccount()); + 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 d926257437e6..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 @@ -34,12 +34,10 @@ requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListZonesCmd extends BaseListCmd implements UserCmd { - private static final String s_name = "listzonesresponse"; - ///////////////////////////////////////////////////// //////////////// 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") @@ -47,28 +45,33 @@ 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, + description = "the name of the storage access group", + since = "4.21.0") + private String storageAccessGroup; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -109,15 +112,22 @@ public Boolean getShowIcon () { return showIcon != null ? showIcon : false; } + public String getStorageAccessGroup() { + return storageAccessGroup; + } + + public ListZonesCmd() { + + } + + public ListZonesCmd(String storageAccessGroup) { + this.storageAccessGroup = storageAccessGroup; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// - @Override - public String getCommandName() { - return s_name; - } - @Override public void execute() { ListResponse response = _queryService.listDataCenters(this); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/utils/OfferingUtils.java b/api/src/main/java/org/apache/cloudstack/api/command/utils/OfferingUtils.java new file mode 100644 index 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/ASNRangeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ASNRangeResponse.java new file mode 100644 index 000000000000..86dab54ca6b8 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/ASNRangeResponse.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.response; + +import com.cloud.bgp.ASNumberRange; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; + +import java.util.Date; + +@EntityReference(value = ASNumberRange.class) +public class ASNRangeResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the AS Number Range") + private String id; + + @SerializedName(ApiConstants.ZONE_ID) + @Param(description = "Zone ID") + private String zoneId; + + @SerializedName(ApiConstants.START_ASN) + @Param(description = "Start AS Number") + private Long startASNumber; + + @SerializedName(ApiConstants.END_ASN) + @Param(description = "End AS Number") + private Long endASNumber; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "Created date") + private Date created; + + public ASNRangeResponse() { + } + + 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 Long getStartASNumber() { + return startASNumber; + } + + public void setStartASNumber(Long startASNumber) { + this.startASNumber = startASNumber; + } + + public Long getEndASNumber() { + return endASNumber; + } + + public void setEndASNumber(Long endASNumber) { + this.endASNumber = endASNumber; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ASNumberResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ASNumberResponse.java new file mode 100644 index 000000000000..45884250984e --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/ASNumberResponse.java @@ -0,0 +1,237 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.bgp.ASNumber; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; + +import java.util.Date; + +@EntityReference(value = ASNumber.class) +public class ASNumberResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the AS Number") + private String id; + + @SerializedName(ApiConstants.ACCOUNT_ID) + @Param(description = "Account ID") + private String accountId; + + @SerializedName(ApiConstants.ACCOUNT) + @Param(description = "the account name") + private String accountName; + + @SerializedName(ApiConstants.DOMAIN_ID) + @Param(description = "Domain ID") + private String domainId; + + @SerializedName(ApiConstants.DOMAIN) + @Param(description = "the domain name") + private String domainName; + + @SerializedName(ApiConstants.AS_NUMBER) + @Param(description = "AS Number") + private Long asNumber; + + @SerializedName(ApiConstants.ASN_RANGE_ID) + @Param(description = "AS Number ID") + private String asNumberRangeId; + + @SerializedName(ApiConstants.ASN_RANGE) + @Param(description = "AS Number Range") + private String asNumberRange; + + @SerializedName(ApiConstants.ZONE_ID) + @Param(description = "Zone ID") + private String zoneId; + + @SerializedName(ApiConstants.ZONE_NAME) + @Param(description = "the zone name of the AS Number range") + private String zoneName; + + @SerializedName("allocated") + @Param(description = "Allocated Date") + private Date allocated; + + @SerializedName(ApiConstants.ALLOCATION_STATE) + @Param(description = "Allocation state") + private String allocationState; + + @SerializedName(ApiConstants.ASSOCIATED_NETWORK_ID) + @Param(description = "Network ID") + private String associatedNetworkId; + + @SerializedName(ApiConstants.ASSOCIATED_NETWORK_NAME) + @Param(description = "Network Name") + private String associatedNetworkName; + + @SerializedName((ApiConstants.VPC_ID)) + @Param(description = "VPC ID") + private String vpcId; + + @SerializedName(ApiConstants.VPC_NAME) + @Param(description = "VPC Name") + private String vpcName; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "Created Date") + private Date created; + + public ASNumberResponse() { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getAccountId() { + return accountId; + } + + public void setAccountId(String accountId) { + this.accountId = accountId; + } + + public String getAccountName() { + return accountName; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + 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 Long getAsNumber() { + return asNumber; + } + + public void setAsNumber(Long asNumber) { + this.asNumber = asNumber; + } + + public String getAsNumberRangeId() { + return asNumberRangeId; + } + + public void setAsNumberRangeId(String asNumberRangeId) { + this.asNumberRangeId = asNumberRangeId; + } + + public String getAsNumberRange() { + return asNumberRange; + } + + public void setAsNumberRange(String asNumberRange) { + this.asNumberRange = asNumberRange; + } + + 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 Date getAllocated() { + return allocated; + } + + public void setAllocated(Date allocatedDate) { + this.allocated = allocatedDate; + } + + public String getAllocationState() { + return allocationState; + } + + public void setAllocationState(String allocated) { + allocationState = allocated; + } + + public String getAssociatedNetworkId() { + return associatedNetworkId; + } + + public void setAssociatedNetworkId(String associatedNetworkId) { + this.associatedNetworkId = associatedNetworkId; + } + + public String getAssociatedNetworkName() { + return associatedNetworkName; + } + + public void setAssociatedNetworkName(String associatedNetworkName) { + this.associatedNetworkName = associatedNetworkName; + } + + public String getVpcId() { + return vpcId; + } + + public void setVpcId(String vpcId) { + this.vpcId = vpcId; + } + + public String getVpcName() { + return vpcName; + } + + public void setVpcName(String vpcName) { + this.vpcName = vpcName; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/AccountResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/AccountResponse.java index 7ffe7d095e44..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,241 +32,309 @@ @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) + @Param(description = "the total number of backups which can be stored by this account", since = "4.21.0") + private String backupLimit; + + @SerializedName(ApiConstants.BACKUP_TOTAL) + @Param(description = "the total number of backups stored by this account", since = "4.21.0") + private Long backupTotal; + + @SerializedName(ApiConstants.BACKUP_AVAILABLE) + @Param(description = "the total number of backups available to this account", since = "4.21.0") + private String backupAvailable; + + @SerializedName(ApiConstants.BACKUP_STORAGE_LIMIT) + @Param(description = "the total backup storage space (in GiB) the account can own", since = "4.21.0") + private String backupStorageLimit; + + @SerializedName(ApiConstants.BACKUP_STORAGE_TOTAL) + @Param(description = "the total backup storage space (in GiB) owned by the account", since = "4.21.0") + private Long backupStorageTotal; + + @SerializedName(ApiConstants.BACKUP_STORAGE_AVAILABLE) + @Param(description = "the total backup storage space (in GiB) available to the account", since = "4.21.0") + 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") + @Param(description = "the total number of gpus the account can own", since = "4.21.0") + private String gpuLimit; + + @SerializedName("gputotal") + @Param(description = "the total number of gpus owned by account", since = "4.21.0") + private Long gpuTotal; + + @SerializedName("gpuavailable") + @Param(description = "the total number of gpus available to be created for this account", since = "4.21.0") + private String gpuAvailable; + @SerializedName("primarystoragelimit") - @Param(description = "the total primary storage space (in GiB) the account can own", since = "4.2.0") + @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) + @Param(description = "the total number of buckets which can be stored by this account", since = "4.21.0") + private String bucketLimit; + + @SerializedName(ApiConstants.BUCKET_TOTAL) + @Param(description = "the total number of buckets stored by this account", since = "4.21.0") + private Long bucketTotal; + + @SerializedName(ApiConstants.BUCKET_AVAILABLE) + @Param(description = "the total number of buckets available to this account", since = "4.21.0") + private String bucketAvailable; + + @SerializedName(ApiConstants.OBJECT_STORAGE_LIMIT) + @Param(description = "the total object storage space (in GiB) the account can own", since = "4.21.0") + private String objectStorageLimit; + + @SerializedName(ApiConstants.OBJECT_STORAGE_TOTAL) + @Param(description = "the total object storage space (in GiB) owned by the account", since = "4.21.0") + private Long objectStorageTotal; + + @SerializedName(ApiConstants.OBJECT_STORAGE_AVAILABLE) + @Param(description = "the total object storage space (in GiB) available to the account", since = "4.21.0") + 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) @Param(description = "Base64 string representation of the resource icon", since = "4.16.0.0") ResourceIconResponse icon; + @SerializedName(ApiConstants.TAGGED_RESOURCES) + @Param(description = "The tagged resource limit and count for the account", since = "4.20.0") + List taggedResources; + + @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") + ApiConstants.ApiKeyAccess apiKeyAccess; + @Override public String getObjectId() { return id; @@ -378,6 +446,36 @@ public void setSnapshotAvailable(String snapshotAvailable) { this.snapshotAvailable = snapshotAvailable; } + @Override + public void setBackupLimit(String backupLimit) { + this.backupLimit = backupLimit; + } + + @Override + public void setBackupTotal(Long backupTotal) { + this.backupTotal = backupTotal; + } + + @Override + public void setBackupAvailable(String backupAvailable) { + this.backupAvailable = backupAvailable; + } + + @Override + public void setBackupStorageLimit(String backupStorageLimit) { + this.backupStorageLimit = backupStorageLimit; + } + + @Override + public void setBackupStorageTotal(Long backupStorageTotal) { + this.backupStorageTotal = backupStorageTotal; + } + + @Override + public void setBackupStorageAvailable(String backupStorageAvailable) { + this.backupStorageAvailable = backupStorageAvailable; + } + @Override public void setTemplateLimit(String templateLimit) { this.templateLimit = templateLimit; @@ -403,6 +501,21 @@ public void setVmRunning(Integer vmRunning) { this.vmRunning = vmRunning; } + @Override + public void setGpuLimit(String gpuLimit) { + this.gpuLimit = gpuLimit; + } + + @Override + public void setGpuTotal(Long gpuTotal) { + this.gpuTotal = gpuTotal; + } + + @Override + public void setGpuAvailable(String gpuAvailable) { + this.gpuAvailable = gpuAvailable; + } + public void setState(String state) { this.state = state; } @@ -456,7 +569,7 @@ public void setNetworkAvailable(String networkAvailable) { @Override public void setVpcLimit(String vpcLimit) { - this.vpcLimit = networkLimit; + this.vpcLimit = vpcLimit; } @Override @@ -529,6 +642,36 @@ public void setSecondaryStorageAvailable(String secondaryStorageAvailable) { this.secondaryStorageAvailable = secondaryStorageAvailable; } + @Override + public void setBucketLimit(String bucketLimit) { + this.bucketLimit = bucketLimit; + } + + @Override + public void setBucketTotal(Long bucketTotal) { + this.bucketTotal = bucketTotal; + } + + @Override + public void setBucketAvailable(String bucketAvailable) { + this.bucketAvailable = bucketAvailable; + } + + @Override + public void setObjectStorageLimit(String objectStorageLimit) { + this.objectStorageLimit = objectStorageLimit; + } + + @Override + public void setObjectStorageTotal(Long objectStorageTotal) { + this.objectStorageTotal = objectStorageTotal; + } + + @Override + public void setObjectStorageAvailable(String objectStorageAvailable) { + this.objectStorageAvailable = objectStorageAvailable; + } + public void setDefaultZone(String defaultZoneId) { this.defaultZoneId = defaultZoneId; } @@ -545,4 +688,13 @@ public void setGroups(List groups) { public void setResourceIconResponse(ResourceIconResponse icon) { this.icon = icon; } + + @Override + public void setTaggedResourceLimitsAndCounts(List taggedResourceLimitsAndCounts) { + this.taggedResources = taggedResourceLimitsAndCounts; + } + + public void setApiKeyAccess(Boolean apiKeyAccess) { + this.apiKeyAccess = ApiConstants.ApiKeyAccess.fromBoolean(apiKeyAccess); + } } 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 7270fa949c8e..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,122 +32,126 @@ @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) + @Param(description = "path of the domain to which the public IP address belongs", since = "4.19.2.0") + 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) { @@ -190,6 +194,11 @@ public void setDomainName(String domainName) { this.domainName = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } + public void setForVirtualNetwork(Boolean forVirtualNetwork) { this.forVirtualNetwork = forVirtualNetwork; } 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 77c4d0d3ffcc..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,23 +43,23 @@ public class AcquirePodIpCmdResponse extends BaseResponse { private String cidrAddress; @SerializedName(ApiConstants.NIC_ID) - @Param(description = "the ID of the nic") - private Long instanceId; + @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) { this.ipAddress = ipAddress; } - public void setInstanceId(Long instanceId) { - this.instanceId = instanceId; + public void setNicId(Long nicId) { + this.nicId = nicId; } public void setPodId(long podId) { @@ -82,8 +82,8 @@ public long getId() { return id; } - public Long getInstanceId() { - return instanceId; + public Long getNicId() { + return nicId; } public long getPodId() { 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 new file mode 100644 index 000000000000..e8c3cf6c4ac7 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/AlertTypeResponse.java @@ -0,0 +1,61 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.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 AlertTypeResponse extends BaseResponse { + + @SerializedName("alerttypeid") + @Param(description = "alert type") + private short alertType; + + @SerializedName(ApiConstants.NAME) + @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; + } + + public void setName(String name) { + this.name = name; + } + + public short getUsageType() { + return alertType; + } + + public void setUsageType(short alertType) { + this.alertType = alertType; + } + + 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 53e3f868a3b7..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,67 +29,71 @@ @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) + @Param(description = "path of the domain to which the Load Balancer belongs", since = "4.19.2.0") + 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 @@ -107,6 +111,11 @@ 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; 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 3eeaaef2afac..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,67 +32,67 @@ 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("jobstatus") - @Param(description = "the current job status-should be 0 for PENDING") - private Integer jobStatus; - @SerializedName("jobprocstatus") - @Param(description = "the progress information of the PENDING job") + @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("managementserverid") - @Param(description = "the msid of the management server on which the job is running", since = "4.19") - private Long msid; + @SerializedName(ApiConstants.MANAGEMENT_SERVER_ID) + @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) + @Param(description = "the management server name of the host", since = "4.21.0") + 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) { @@ -119,11 +119,6 @@ public void setCmd(String cmd) { this.cmd = cmd; } - @Override - public void setJobStatus(Integer jobStatus) { - this.jobStatus = jobStatus; - } - public void setJobProcStatus(Integer jobProcStatus) { this.jobProcStatus = jobProcStatus; } @@ -156,7 +151,11 @@ public void setRemoved(final Date removed) { this.removed = removed; } - public void setMsid(Long msid) { - this.msid = msid; + public void setManagementServerId(String managementServerId) { + this.managementServerId = managementServerId; + } + + public void setManagementServerName(String managementServerName) { + this.managementServerName = managementServerName; } } 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 ae3462f1fec2..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,49 +31,53 @@ 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) + @Param(description = "path of the domain to which the autoscale policy belongs", since = "4.19.2.0") + private String domainPath; + @Override public String getObjectId() { return this.id; @@ -118,6 +122,11 @@ 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; 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 656a62ddda35..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,103 +32,107 @@ 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) + @Param(description = "path of the domain to which the vm group belongs", since = "4.19.2.0") + 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() { @@ -227,6 +231,11 @@ 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; 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 9f2383447301..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 = "Base 64 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,37 +85,41 @@ 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) + @Param(description = "path of the domain to which the vm profile belongs", since = "4.19.2.0") + 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() { @@ -196,6 +200,10 @@ 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; 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 480ebcfb13d3..c4f3ee31dadc 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 @@ -34,31 +34,49 @@ 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) + @Param(description = "provider name", since = "4.21.0") + 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; public void setId(String id) { @@ -69,6 +87,10 @@ public void setExternalId(String externalId) { this.externalId = externalId; } + public void setProvider(String provider) { + this.provider = provider; + } + public void setName(String name) { this.name = name; } @@ -89,7 +111,20 @@ 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; + } + } 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 new file mode 100644 index 000000000000..0d3c830950b2 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupRepositoryResponse.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.response; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.backup.BackupRepository; + +import java.util.Date; + +@EntityReference(value = BackupRepository.class) +public class BackupRepositoryResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "the ID of the backup repository") + private String id; + + @SerializedName(ApiConstants.ZONE_ID) + @Param(description = "the Zone ID of the backup repository") + private String zoneId; + + @SerializedName(ApiConstants.ZONE_NAME) + @Param(description = "the Zone name of the backup repository") + private String zoneName; + + @SerializedName(ApiConstants.NAME) + @Param(description = "the name of the backup repository") + private String name; + + @SerializedName(ApiConstants.ADDRESS) + @Param(description = "the address / url of the backup repository") + private String address; + + @SerializedName(ApiConstants.PROVIDER) + @Param(description = "name of the provider") + private String providerName; + + @SerializedName(ApiConstants.TYPE) + @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; + + public BackupRepositoryResponse() { + } + + 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 getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getProviderName() { + return providerName; + } + + public void setProviderName(String providerName) { + this.providerName = providerName; + } + + public String getType() { + return type; + } + + 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; + } + + 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; + } + + public void setCreated(Date created) { + this.created = 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 63419680fea3..b855bfe40b8d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/BackupResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupResponse.java @@ -26,82 +26,107 @@ import com.google.gson.annotations.SerializedName; import java.util.Date; +import java.util.Map; @EntityReference(value = Backup.class) 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) + @Param(description = "name of the backup", since = "4.21.0") + private String name; + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "description for the backup", since = "4.21.0") + private String description; + @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) - @Param(description = "ID of the VM") + @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.STATUS) - @Param(description = "backup status") + @Param(description = "Backup status") private Backup.Status status; @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) + @Param(description = "Lists the vm specific details for the backup", since = "4.21.0") + private Map vmDetails; + + @SerializedName(ApiConstants.INTERVAL_TYPE) + @Param(description = "Interval type of the backup", since = "4.21.0") + private String intervalType; + + @SerializedName(ApiConstants.BACKUP_VM_OFFERING_REMOVED) + @Param(description = "The backup offering corresponding to this backup was removed from the VM", since = "4.21.0") + private Boolean vmOfferingRemoved; + + @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; + public String getId() { return id; } @@ -110,6 +135,22 @@ 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 getVmId() { return vmId; } @@ -245,4 +286,32 @@ public String getZone() { public void setZone(String zone) { this.zone = zone; } + + public Map getVmDetails() { + return vmDetails; + } + + public void setVmDetails(Map vmDetails) { + this.vmDetails = vmDetails; + } + + public String getIntervalType() { + return this.intervalType; + } + + public void setIntervalType(String intervalType) { + this.intervalType = intervalType; + } + + public Boolean getVmOfferingRemoved() { + return this.vmOfferingRemoved; + } + + public void setVmOfferingRemoved(Boolean vmOfferingRemoved) { + this.vmOfferingRemoved = vmOfferingRemoved; + } + + public void setVmExpunged(Boolean isVmExpunged) { + this.isVmExpunged = isVmExpunged; + } } 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 ba44f1e024f7..13d0c5d8c562 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/BackupScheduleResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupScheduleResponse.java @@ -28,27 +28,42 @@ @EntityReference(value = BackupSchedule.class) public class BackupScheduleResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the backup schedule.", since = "4.21.0") + private String id; @SerializedName(ApiConstants.VIRTUAL_MACHINE_NAME) - @Param(description = "name of the VM") + @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("schedule") - @Param(description = "time the backup is scheduled to be taken.") + @SerializedName(ApiConstants.SCHEDULE) + @Param(description = "The time the backup is scheduled to be taken.") private String schedule; - @SerializedName("intervaltype") - @Param(description = "the interval type of the backup schedule") + @SerializedName(ApiConstants.INTERVAL_TYPE) + @Param(description = "The interval type of the backup schedule") private DateUtil.IntervalType intervalType; - @SerializedName("timezone") - @Param(description = "the time zone of the backup schedule") + @SerializedName(ApiConstants.TIMEZONE) + @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; + + public void setId(String id) { + this.id = id; + } + + @SerializedName(ApiConstants.QUIESCE_VM) + @Param(description = "quiesce the instance before checkpointing the disks for backup") + private Boolean quiesceVM; + public String getVmName() { return vmName; } @@ -88,4 +103,12 @@ public String getTimezone() { public void setTimezone(String timezone) { this.timezone = timezone; } + + public void setMaxBackups(Integer maxBackups) { + this.maxBackups = maxBackups; + } + + public void setQuiesceVM(Boolean quiesceVM) { + this.quiesceVM = quiesceVM; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/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/BgpPeerResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BgpPeerResponse.java new file mode 100644 index 000000000000..344e65c6badc --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/BgpPeerResponse.java @@ -0,0 +1,200 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package 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.network.BgpPeer; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +@EntityReference(value = BgpPeer.class) +public class BgpPeerResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) + @Param(description = "id of the bgp peer") + private String id; + + @SerializedName(ApiConstants.IP_ADDRESS) + @Param(description = "IPv4 address of bgp peer") + private String ip4Address; + + @SerializedName(ApiConstants.IP6_ADDRESS) + @Param(description = "IPv6 address of bgp peer") + private String ip6Address; + + @SerializedName(ApiConstants.AS_NUMBER) + @Param(description = "AS number of bgp peer") + private Long asNumber; + + @SerializedName(ApiConstants.PASSWORD) + @Param(description = "password of bgp peer") + private String password; + + @SerializedName(ApiConstants.ZONE_ID) + @Param(description = "id of zone to which the bgp peer belongs to." ) + private String zoneId; + + @SerializedName(ApiConstants.ZONE_NAME) + @Param(description = "name of zone to which the bgp peer belongs to." ) + private String zoneName; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "date when this bgp peer was created." ) + private Date created; + + @SerializedName(ApiConstants.ACCOUNT) + @Param(description = "the account of the bgp peer") + private String accountName; + + @SerializedName(ApiConstants.DOMAIN_ID) + @Param(description = "the domain ID of the bgp peer") + private String domainId; + + @SerializedName(ApiConstants.DOMAIN) + @Param(description = "the domain name of the bgp peer") + private String domainName; + + @SerializedName(ApiConstants.PROJECT_ID) + @Param(description = "the project id of the bgp peer") + private String projectId; + + @SerializedName(ApiConstants.PROJECT) + @Param(description = "the project name of the bgp peer") + private String projectName; + + @SerializedName(ApiConstants.DETAILS) + @Param(description = "additional key/value details of the bgp peer") + private Map details; + + public void setId(String id) { + this.id = id; + } + + public void setIp4Address(String ip4Address) { + this.ip4Address = ip4Address; + } + + public void setIp6Address(String ip6Address) { + this.ip6Address = ip6Address; + } + + public void setAsNumber(Long asNumber) { + this.asNumber = asNumber; + } + + public void setPassword(String password) { + this.password = password; + } + + public void setZoneId(String zoneId) { + this.zoneId = zoneId; + } + + public void setZoneName(String zoneName) { + this.zoneName = zoneName; + } + + public void setCreated(Date created) { + this.created = created; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public void setProjectId(String projectId) { + this.projectId = projectId; + } + + public void setProjectName(String projectName) { + this.projectName = projectName; + } + + public void setDomainId(String domainId) { + this.domainId = domainId; + } + + public void setDomainName(String domainName) { + this.domainName = domainName; + } + + public void setDetails(Map details) { + this.details = details; + } + + public String getId() { + return id; + } + + public String getIp4Address() { + return ip4Address; + } + + public String getIp6Address() { + return ip6Address; + } + + public Long getAsNumber() { + return asNumber; + } + + public String getPassword() { + return password; + } + + public String getZoneId() { + return zoneId; + } + + public String getZoneName() { + return zoneName; + } + + public Date getCreated() { + return created; + } + + public String getAccountName() { + return accountName; + } + + public String getDomainId() { + return domainId; + } + + public String getDomainName() { + return domainName; + } + + public String getProjectId() { + return projectId; + } + + public String getProjectName() { + return projectName; + } + + public Map getDetails() { + return details; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/BucketResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BucketResponse.java index b75f36043244..cde140839ec0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/BucketResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/BucketResponse.java @@ -54,6 +54,10 @@ public class BucketResponse extends BaseResponseWithTagInformation implements Co @SerializedName(ApiConstants.DOMAIN) @Param(description = "the domain associated with the bucket") private String domainName; + + @SerializedName(ApiConstants.DOMAIN_PATH) + @Param(description = "path of the domain to which the bucket belongs", since = "4.19.2.0") + private String domainPath; @SerializedName(ApiConstants.OBJECT_STORAGE_ID) @Param(description = "id of the object storage hosting the Bucket; returned to admin user only") private String objectStoragePoolId; @@ -71,7 +75,7 @@ public class BucketResponse extends BaseResponseWithTagInformation implements Co private String state; @SerializedName(ApiConstants.QUOTA) - @Param(description = "Bucket Quota in GB") + @Param(description = "Bucket Quota in GiB") private Integer quota; @SerializedName(ApiConstants.ENCRYPTION) @@ -98,7 +102,7 @@ public class BucketResponse extends BaseResponseWithTagInformation implements Co @Param(description = "Bucket Access Key") private String accessKey; - @SerializedName(ApiConstants.SECRET_KEY) + @SerializedName(ApiConstants.USER_SECRET_KEY) @Param(description = "Bucket Secret Key") private String secretKey; @@ -146,6 +150,11 @@ 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; 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 e4224c85e970..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,9 @@ // 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; @@ -25,79 +28,87 @@ @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) + @Param(description = "true if users are allowed to force stop a vm, false otherwise", since = "4.20.0") + 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") @@ -105,25 +116,53 @@ 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) + @Param(description = "the min CPU count for the service offering used by the shared filesystem instance", since = "4.20.0") + private Integer sharedFsVmMinCpuCount; + + @SerializedName(ApiConstants.SHAREDFSVM_MIN_RAM_SIZE) + @Param(description = "the min Ram size for the service offering used by the shared filesystem instance", since = "4.20.0") + private Integer sharedFsVmMinRamSize; + + @SerializedName(ApiConstants.INSTANCE_LEASE_ENABLED) + @Param(description = "true if instance lease feature is enabled", since = "4.21.0") + private Boolean instanceLeaseEnabled; + + @SerializedName(ApiConstants.EXTENSIONS_PATH) + @Param(description = "The path of the extensions directory", since = "4.21.0", authorized = {RoleType.Admin}) + private String extensionsPath; + + @SerializedName(ApiConstants.DYNAMIC_SCALING_ENABLED) + @Param(description = "true if dynamically scaling for instances is enabled", since = "4.21.0") + private Boolean dynamicScalingEnabled; + + @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; } @@ -168,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; } @@ -192,6 +235,10 @@ public void setAllowUserViewAllDomainAccounts(boolean allowUserViewAllDomainAcco this.allowUserViewAllDomainAccounts = allowUserViewAllDomainAccounts; } + public void setAllowUserForceStopVM(boolean allowUserForceStopVM) { + this.allowUserForceStopVM = allowUserForceStopVM; + } + public void setKubernetesServiceEnabled(boolean kubernetesServiceEnabled) { this.kubernetesServiceEnabled = kubernetesServiceEnabled; } @@ -223,4 +270,32 @@ public void setInstancesDisksStatsRetentionTime(Integer instancesDisksStatsReten public void setCustomHypervisorDisplayName(String customHypervisorDisplayName) { this.customHypervisorDisplayName = customHypervisorDisplayName; } + + public void setSharedFsVmMinCpuCount(Integer sharedFsVmMinCpuCount) { + this.sharedFsVmMinCpuCount = sharedFsVmMinCpuCount; + } + + public void setSharedFsVmMinRamSize(Integer sharedFsVmMinRamSize) { + this.sharedFsVmMinRamSize = sharedFsVmMinRamSize; + } + + public void setInstanceLeaseEnabled(Boolean instanceLeaseEnabled) { + this.instanceLeaseEnabled = instanceLeaseEnabled; + } + + public void setExtensionsPath(String extensionsPath) { + this.extensionsPath = extensionsPath; + } + + public void setDynamicScalingEnabled(Boolean dynamicScalingEnabled) { + this.dynamicScalingEnabled = dynamicScalingEnabled; + } + + 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 e9724497c38b..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 @@ -16,62 +16,65 @@ // under the License. package org.apache.cloudstack.api.response; -import com.google.gson.annotations.SerializedName; - import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; 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) + @Param(description = "The tag for the capacity type", since = "4.20.0") + private String tag; + public Short getCapacityType() { return capacityType; } @@ -167,4 +170,8 @@ public String getPercentUsed() { public void setPercentUsed(String percentUsed) { this.percentUsed = percentUsed; } + + public void setTag(String tag) { + this.tag = tag; + } } 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/ClusterDrsPlanMigrationResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ClusterDrsPlanMigrationResponse.java index 4114c228e265..f399c1260f77 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ClusterDrsPlanMigrationResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ClusterDrsPlanMigrationResponse.java @@ -50,13 +50,13 @@ public class ClusterDrsPlanMigrationResponse extends BaseResponse { @Param(description = "Destination host for VM migration") String destHostName; - @SerializedName(ApiConstants.JOB_ID) + @SerializedName(ApiConstants.MIGRATION_JOB_ID) @Param(description = "id of VM migration async job") - private Long jobId; + private Long migrationJobId; - @SerializedName(ApiConstants.JOB_STATUS) + @SerializedName(ApiConstants.MIGRATION_JOB_STATUS) @Param(description = "Job status of VM migration async job") - private JobInfo.Status jobStatus; + private JobInfo.Status migrationJobStatus; public ClusterDrsPlanMigrationResponse(String vmId, String vmName, String srcHostId, String srcHostName, @@ -68,8 +68,8 @@ public ClusterDrsPlanMigrationResponse(String vmId, String vmName, String srcHos this.srcHostName = srcHostName; this.destHostId = destHostId; this.destHostName = destHostName; - this.jobId = jobId; - this.jobStatus = jobStatus; + this.migrationJobId = jobId; + this.migrationJobStatus = jobStatus; this.setObjectName(ApiConstants.MIGRATIONS); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ClusterResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ClusterResponse.java index 72dab3da3b19..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 @@ -31,52 +31,54 @@ @EntityReference(value = Cluster.class) 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) - private List capacitites; + @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") @@ -91,6 +93,38 @@ public class ClusterResponse extends BaseResponseWithAnnotations { @Param(description = "Meta data associated with the zone (key/value pairs)") private Map resourceDetails; + @SerializedName(ApiConstants.ARCH) + @Param(description = "CPU Arch of the hosts in the cluster", since = "4.20") + private String arch; + + @SerializedName(ApiConstants.STORAGE_ACCESS_GROUPS) + @Param(description = "comma-separated list of storage access groups for the host", since = "4.21.0") + private String storageAccessGroups; + + @SerializedName(ApiConstants.POD_STORAGE_ACCESS_GROUPS) + @Param(description = "comma-separated list of storage access groups on the pod", since = "4.21.0") + private String podStorageAccessGroups; + + @SerializedName(ApiConstants.ZONE_STORAGE_ACCESS_GROUPS) + @Param(description = "comma-separated list of storage access groups on the zone", since = "4.21.0") + private String zoneStorageAccessGroups; + + @SerializedName(ApiConstants.EXTENSION_ID) + @Param(description="The ID of extension for this cluster", since = "4.21.0") + private String extensionId; + + @SerializedName(ApiConstants.EXTENSION_NAME) + @Param(description="The name of extension for this cluster", since = "4.21.0") + private String extensionName; + + public void setInternalId(long internalId) { + this.internalId = internalId; + } + + public long getInternalId() { + return internalId; + } + public String getId() { return id; } @@ -171,12 +205,12 @@ public void setManagedState(String managedState) { this.managedState = managedState; } - public List getCapacitites() { - return capacitites; + public List getCapacities() { + return capacities; } - public void setCapacitites(ArrayList arrayList) { - this.capacitites = arrayList; + public void setCapacities(ArrayList arrayList) { + this.capacities = arrayList; } public void setCpuOvercommitRatio(String cpuovercommitratio) { @@ -219,4 +253,80 @@ public void setResourceDetails(Map details) { public Map getResourceDetails() { return resourceDetails; } + + public String getCpuovercommitratio() { + return cpuovercommitratio; + } + + public void setCpuovercommitratio(String cpuovercommitratio) { + this.cpuovercommitratio = cpuovercommitratio; + } + + public String getMemoryovercommitratio() { + return memoryovercommitratio; + } + + public void setMemoryovercommitratio(String memoryovercommitratio) { + this.memoryovercommitratio = memoryovercommitratio; + } + + public String getOvm3vip() { + return ovm3vip; + } + + public void setOvm3vip(String ovm3vip) { + this.ovm3vip = ovm3vip; + } + + public void setCapacities(List capacities) { + this.capacities = capacities; + } + + public void setArch(String arch) { + this.arch = arch; + } + + public String getArch() { + return arch; + } + + public String getStorageAccessGroups() { + return storageAccessGroups; + } + + public void setStorageAccessGroups(String storageAccessGroups) { + this.storageAccessGroups = storageAccessGroups; + } + + public String getPodStorageAccessGroups() { + return podStorageAccessGroups; + } + + public void setPodStorageAccessGroups(String podStorageAccessGroups) { + this.podStorageAccessGroups = podStorageAccessGroups; + } + + public String getZoneStorageAccessGroups() { + return zoneStorageAccessGroups; + } + + public void setZoneStorageAccessGroups(String zoneStorageAccessGroups) { + this.zoneStorageAccessGroups = zoneStorageAccessGroups; + } + + public void setExtensionId(String extensionId) { + this.extensionId = extensionId; + } + + public String getExtensionId() { + return extensionId; + } + + public void setExtensionName(String extensionName) { + this.extensionName = extensionName; + } + + public String getExtensionName() { + return extensionName; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ConditionResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ConditionResponse.java index 15671430bf17..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,27 +54,31 @@ 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) + @Param(description = "path of the domain to which the Condition owner belongs", since = "4.19.2.0") + 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; // ///////////////////////////////////////////////// @@ -138,4 +142,9 @@ public void setDomainId(String domainId) { public void setDomainName(String domainName) { this.domain = domainName; } + + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } } 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/ConsoleSessionResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ConsoleSessionResponse.java new file mode 100644 index 000000000000..85747d7c2a8f --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/ConsoleSessionResponse.java @@ -0,0 +1,236 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License.import org.apache.cloudstack.context.CallContext; +package org.apache.cloudstack.api.response; + +import com.google.gson.annotations.SerializedName; + +import com.cloud.serializer.Param; +import org.apache.cloudstack.consoleproxy.ConsoleSession; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; + +import java.util.Date; + +@EntityReference(value = ConsoleSession.class) +public class ConsoleSessionResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the console session.") + private String id; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "Date when the console session's endpoint was created.") + private Date created; + + @SerializedName(ApiConstants.DOMAIN) + @Param(description = "Domain of the account that created the console endpoint.") + private String domain; + + @SerializedName(ApiConstants.DOMAIN_PATH) + @Param(description = "Domain path of the account that created the console endpoint.") + private String domainPath; + + @SerializedName(ApiConstants.DOMAIN_ID) + @Param(description = "Domain ID of the account that created the console endpoint.") + private String domainId; + + @SerializedName(ApiConstants.ACCOUNT) + @Param(description = "Account that created the console endpoint.") + private String account; + + @SerializedName(ApiConstants.ACCOUNT_ID) + @Param(description = "ID of the account that created the console endpoint.") + private String accountId; + + @SerializedName(ApiConstants.USER) + @Param(description = "User that created the console endpoint.") + private String user; + + @SerializedName(ApiConstants.USER_ID) + @Param(description = "ID of the user that created the console endpoint.") + private String userId; + + @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) + @Param(description = "ID of the virtual machine.") + private String vmId; + + @SerializedName(ApiConstants.VIRTUAL_MACHINE_NAME) + @Param(description = "Name of the virtual machine.") + private String vmName; + + @SerializedName(ApiConstants.HOST_ID) + @Param(description = "ID of the host.") + private String hostId; + + @SerializedName(ApiConstants.HOST_NAME) + @Param(description = "Name of the host.") + private String hostName; + + @SerializedName(ApiConstants.ACQUIRED) + @Param(description = "Date when the console session was acquired.") + private Date acquired; + + @SerializedName(ApiConstants.REMOVED) + @Param(description = "Date when the console session was removed.") + private Date removed; + + @SerializedName(ApiConstants.CONSOLE_ENDPOINT_CREATOR_ADDRESS) + @Param(description = "IP address of the creator of the console endpoint.") + private String consoleEndpointCreatorAddress; + + @SerializedName(ApiConstants.CLIENT_ADDRESS) + @Param(description = "IP address of the client that created the console session.") + private String clientAddress; + + public void setId(String id) { + this.id = id; + } + + public void setCreated(Date created) { + this.created = created; + } + + public void setDomain(String domain) { + this.domain = domain; + } + + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } + + public void setDomainId(String domainId) { + this.domainId = domainId; + } + + public void setAccount(String account) { + this.account = account; + } + + public void setAccountId(String accountId) { + this.accountId = accountId; + } + + public void setUser(String user) { + this.user = user; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public void setVmId(String vmId) { + this.vmId = vmId; + } + + public void setVmName(String vmName) { + this.vmName = vmName; + } + + public void setHostId(String hostId) { + this.hostId = hostId; + } + + public void setHostName(String hostName) { + this.hostName = hostName; + } + + public void setAcquired(Date acquired) { + this.acquired = acquired; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } + + public void setConsoleEndpointCreatorAddress(String consoleEndpointCreatorAddress) { + this.consoleEndpointCreatorAddress = consoleEndpointCreatorAddress; + } + + public void setClientAddress(String clientAddress) { + this.clientAddress = clientAddress; + } + + public String getId() { + return id; + } + + public Date getCreated() { + return created; + } + + public String getDomain() { + return domain; + } + + public String getDomainPath() { + return domainPath; + } + + public String getDomainId() { + return domainId; + } + + public String getAccount() { + return account; + } + + public String getAccountId() { + return accountId; + } + + public String getUser() { + return user; + } + + public String getUserId() { + return userId; + } + + public String getVmId() { + return vmId; + } + + public String getVmName() { + return vmName; + } + + public String getHostId() { + return hostId; + } + + public String getHostName() { + return hostName; + } + + public Date getAcquired() { + return acquired; + } + + public Date getRemoved() { + return removed; + } + + public String getConsoleEndpointCreatorAddress() { + return consoleEndpointCreatorAddress; + } + + public String getClientAddress() { + return clientAddress; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ControlledEntityResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ControlledEntityResponse.java index 598ef0822539..dc021705d78a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ControlledEntityResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ControlledEntityResponse.java @@ -27,4 +27,6 @@ public interface ControlledEntityResponse { public void setDomainId(String domainId); public void setDomainName(String domainName); + + public void setDomainPath(String domainPath); } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ControlledViewEntityResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ControlledViewEntityResponse.java index abe4dd771430..730c2c38fc57 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ControlledViewEntityResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ControlledViewEntityResponse.java @@ -27,4 +27,6 @@ public interface ControlledViewEntityResponse { public void setDomainId(String domainId); public void setDomainName(String domainName); + + public void setDomainPath(String domainPath); } 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 c60917bbe7a2..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 @@ -26,20 +26,20 @@ public class CreateConsoleEndpointResponse extends BaseResponse { public CreateConsoleEndpointResponse() { } - @SerializedName(ApiConstants.RESULT) - @Param(description = "true if the console endpoint is generated properly") + @SerializedName(ApiConstants.SUCCESS) + @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/DataCenterIpv4SubnetResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DataCenterIpv4SubnetResponse.java new file mode 100644 index 000000000000..a1a87794a888 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/DataCenterIpv4SubnetResponse.java @@ -0,0 +1,151 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +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.datacenter.DataCenterIpv4GuestSubnet; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +@EntityReference(value = DataCenterIpv4GuestSubnet.class) +public class DataCenterIpv4SubnetResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) + @Param(description = "id of the guest IPv4 subnet") + private String id; + + @SerializedName(ApiConstants.SUBNET) + @Param(description = "guest IPv4 subnet") + private String subnet; + + @SerializedName(ApiConstants.ZONE_ID) + @Param(description = "id of zone to which the IPv4 subnet belongs to." ) + private String zoneId; + + @SerializedName(ApiConstants.ZONE_NAME) + @Param(description = "name of zone to which the IPv4 subnet belongs to." ) + private String zoneName; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "date when this IPv4 subnet was created." ) + private Date created; + + @SerializedName(ApiConstants.ACCOUNT) + @Param(description = "the account of the IPv4 subnet") + private String accountName; + + @SerializedName(ApiConstants.DOMAIN_ID) + @Param(description = "the domain ID of the IPv4 subnet") + private String domainId; + + @SerializedName(ApiConstants.DOMAIN) + @Param(description = "the domain name of the IPv4 subnet") + private String domainName; + + @SerializedName(ApiConstants.PROJECT_ID) + @Param(description = "the project id of the IPv4 subnet") + private String projectId; + + @SerializedName(ApiConstants.PROJECT) + @Param(description = "the project name of the IPv4 subnet") + private String projectName; + + public void setId(String id) { + this.id = id; + } + + public void setSubnet(String subnet) { + this.subnet = subnet; + } + + public void setZoneId(String zoneId) { + this.zoneId = zoneId; + } + + public void setZoneName(String zoneName) { + this.zoneName = zoneName; + } + + public void setCreated(Date created) { + this.created = created; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public void setProjectId(String projectId) { + this.projectId = projectId; + } + + public void setProjectName(String projectName) { + this.projectName = projectName; + } + + public void setDomainId(String domainId) { + this.domainId = domainId; + } + + public void setDomainName(String domainName) { + this.domainName = domainName; + } + + public String getId() { + return id; + } + + public String getSubnet() { + return subnet; + } + + public String getZoneId() { + return zoneId; + } + + public String getZoneName() { + return zoneName; + } + + public Date getCreated() { + return created; + } + + public String getAccountName() { + return accountName; + } + + public String getDomainId() { + return domainId; + } + + public String getDomainName() { + return domainName; + } + + public String getProjectId() { + return projectId; + } + + public String getProjectName() { + return projectName; + } +} 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 b8244aebc608..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,130 +30,134 @@ @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) + @Param(description = "state of the disk offering") + 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; @@ -166,9 +170,13 @@ 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) + @Param(description = "Returns true if the disk offering is suitable for the given virtual machine for disk creation otherwise false", since = "4.20.0") + private Boolean suitableForVm; + public Boolean getDisplayOffering() { return displayOffering; } @@ -226,6 +234,14 @@ public void setName(String name) { this.name = name; } + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + public String getDisplayText() { return displayText; } @@ -391,4 +407,8 @@ public void setDiskSizeStrictness(Boolean diskSizeStrictness) { public void setDetails(Map details) { this.details = details; } + + public void setSuitableForVm(Boolean suitableForVm) { + this.suitableForVm = suitableForVm; + } } 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 e4e409a40ee1..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,174 +16,236 @@ // 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.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("templatelimit") @Param(description="the total number of templates which can be created by this domain") + @SerializedName(ApiConstants.BACKUP_LIMIT) + @Param(description = "the total number of backups which can be stored by this domain", since = "4.21.0") + private String backupLimit; + + @SerializedName(ApiConstants.BACKUP_TOTAL) + @Param(description = "the total number of backups stored by this domain", since = "4.21.0") + private Long backupTotal; + + @SerializedName(ApiConstants.BACKUP_AVAILABLE) + @Param(description = "the total number of backups available to this domain", since = "4.21.0") + private String backupAvailable; + + @SerializedName(ApiConstants.BACKUP_STORAGE_LIMIT) + @Param(description = "the total backup storage space (in GiB) the domain can own", since = "4.21.0") + private String backupStorageLimit; + + @SerializedName(ApiConstants.BACKUP_STORAGE_TOTAL) + @Param(description = "the total backup storage space (in GiB) owned by the domain", since = "4.21.0") + private Long backupStorageTotal; + + @SerializedName(ApiConstants.BACKUP_STORAGE_AVAILABLE) + @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") 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("primarystoragelimit") @Param(description="the total primary storage space (in GiB) the domain can own", since="4.2.0") + @SerializedName("gpulimit") @Param(description="the total number of gpus the domain can own", since="4.21.0") + private String gpuLimit; + + @SerializedName("gputotal") @Param(description="the total number of gpus owned by domain", since="4.21.0") + private Long gpuTotal; + + @SerializedName("gpuavailable") @Param(description="the total number of gpus available to be created for this domain", since="4.21.0") + private String gpuAvailable; + + @SerializedName("primarystoragelimit") @Param(description = "The total primary storage space (in GiB) the domain can own", since="4.2.0") private String primaryStorageLimit; - @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) + @Param(description = "the total number of buckets which can be stored by this domain", since = "4.21.0") + private String bucketLimit; + + @SerializedName(ApiConstants.BUCKET_TOTAL) + @Param(description = "the total number of buckets stored by this domain", since = "4.21.0") + private Long bucketTotal; + + @SerializedName(ApiConstants.BUCKET_AVAILABLE) + @Param(description = "the total number of buckets available to this domain", since = "4.21.0") + private String bucketAvailable; + + @SerializedName(ApiConstants.OBJECT_STORAGE_LIMIT) + @Param(description = "the total object storage space (in GiB) the domain can own", since = "4.21.0") + private String objectStorageLimit; + + @SerializedName(ApiConstants.OBJECT_STORAGE_TOTAL) + @Param(description = "the total object storage space (in GiB) owned by the domain", since = "4.21.0") + private Long objectStorageTotal; + + @SerializedName(ApiConstants.OBJECT_STORAGE_AVAILABLE) + @Param(description = "the total object storage space (in GiB) available to the domain", since = "4.21.0") + private String objectStorageAvailable; + @SerializedName(ApiConstants.RESOURCE_ICON) @Param(description = "Base64 string representation of the resource icon", since = "4.16.0.0") 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) + @Param(description = "The tagged resource limit and count for the domain", since = "4.20.0") + List taggedResources; + public String getId() { return this.id; } @@ -308,6 +370,36 @@ public void setSnapshotAvailable(String snapshotAvailable) { this.snapshotAvailable = snapshotAvailable; } + @Override + public void setBackupLimit(String backupLimit) { + this.backupLimit = backupLimit; + } + + @Override + public void setBackupTotal(Long backupTotal) { + this.backupTotal = backupTotal; + } + + @Override + public void setBackupAvailable(String backupAvailable) { + this.backupAvailable = backupAvailable; + } + + @Override + public void setBackupStorageLimit(String backupStorageLimit) { + this.backupStorageLimit = backupStorageLimit; + } + + @Override + public void setBackupStorageTotal(Long backupStorageTotal) { + this.backupStorageTotal = backupStorageTotal; + } + + @Override + public void setBackupStorageAvailable(String backupStorageAvailable) { + this.backupStorageAvailable = backupStorageAvailable; + } + @Override public void setTemplateLimit(String templateLimit) { this.templateLimit = templateLimit; @@ -395,6 +487,21 @@ public void setMemoryAvailable(String memoryAvailable) { this.memoryAvailable = memoryAvailable; } + @Override + public void setGpuLimit(String gpuLimit) { + this.gpuLimit = gpuLimit; + } + + @Override + public void setGpuTotal(Long gpuTotal) { + this.gpuTotal = gpuTotal; + } + + @Override + public void setGpuAvailable(String gpuAvailable) { + this.gpuAvailable = gpuAvailable; + } + @Override public void setPrimaryStorageLimit(String primaryStorageLimit) { this.primaryStorageLimit = primaryStorageLimit; @@ -425,6 +532,36 @@ public void setSecondaryStorageAvailable(String secondaryStorageAvailable) { this.secondaryStorageAvailable = secondaryStorageAvailable; } + @Override + public void setBucketLimit(String bucketLimit) { + this.bucketLimit = bucketLimit; + } + + @Override + public void setBucketTotal(Long bucketTotal) { + this.bucketTotal = bucketTotal; + } + + @Override + public void setBucketAvailable(String bucketAvailable) { + this.bucketAvailable = bucketAvailable; + } + + @Override + public void setObjectStorageLimit(String objectStorageLimit) { + this.objectStorageLimit = objectStorageLimit; + } + + @Override + public void setObjectStorageTotal(Long objectStorageTotal) { + this.objectStorageTotal = objectStorageTotal; + } + + @Override + public void setObjectStorageAvailable(String objectStorageAvailable) { + this.objectStorageAvailable = objectStorageAvailable; + } + public void setState(String state) { this.state = state; } @@ -447,4 +584,13 @@ public void setResourceIconResponse(ResourceIconResponse icon) { public void setDetails(Map details) { this.details = details; } + + @Override + 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 99e5f6ccdfac..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,179 +34,183 @@ @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) + @Param(description = "path of the Domain the router belongs to", since = "4.19.2.0") + 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) @@ -214,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") @@ -238,9 +242,13 @@ 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) + @Param(description = "CPU arch of the router", since = "4.20.1") + private String arch; + public DomainRouterResponse() { nics = new LinkedHashSet(); } @@ -381,6 +389,10 @@ public void setDomainName(String domainName) { this.domainName = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } public void setPublicNetworkId(String publicNetworkId) { this.publicNetworkId = publicNetworkId; } @@ -510,4 +522,8 @@ public String getSoftwareVersion() { public void setSoftwareVersion(String softwareVersion) { this.softwareVersion = softwareVersion; } + + public void setArch(String arch) { + this.arch = arch; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/EventResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/EventResponse.java index 8f65492cb705..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,71 +30,75 @@ @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) + @Param(description = "path of the Domain the account's domain belongs to", since = "4.19.2.0") + 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) { @@ -132,6 +136,11 @@ public void setDomainName(String domainName) { this.domainName = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } + public void setResourceId(String resourceId) { this.resourceId = resourceId; } 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/ExtensionCustomActionParameterResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ExtensionCustomActionParameterResponse.java new file mode 100644 index 000000000000..d627f8077dc1 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/ExtensionCustomActionParameterResponse.java @@ -0,0 +1,58 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.response; + +import java.util.List; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class ExtensionCustomActionParameterResponse extends BaseResponse { + + @SerializedName(ApiConstants.NAME) + @Param(description = "Name of the parameter") + private String name; + + @SerializedName(ApiConstants.TYPE) + @Param(description = "Type of the parameter") + private String type; + + @SerializedName(ApiConstants.VALIDATION_FORMAT) + @Param(description = "Validation format for value of the parameter. Available for specific types") + private String validationFormat; + + @SerializedName(ApiConstants.VALUE_OPTIONS) + @Param(description = "Comma-separated list of options for value of the parameter") + private List valueOptions; + + @SerializedName(ApiConstants.REQUIRED) + @Param(description = "Whether the parameter is required or not") + private Boolean required; + + public ExtensionCustomActionParameterResponse(String name, String type, String validationFormat, List valueOptions, + boolean required) { + this.name = name; + this.type = type; + this.validationFormat = validationFormat; + this.valueOptions = valueOptions; + this.required = required; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ExtensionCustomActionResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ExtensionCustomActionResponse.java new file mode 100644 index 000000000000..96edf6d2fd80 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/ExtensionCustomActionResponse.java @@ -0,0 +1,184 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.response; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.extension.ExtensionCustomAction; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +@EntityReference(value = ExtensionCustomAction.class) +public class ExtensionCustomActionResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the custom action") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "Name of the custom action") + private String name; + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "Description of the custom action") + private String description; + + @SerializedName(ApiConstants.EXTENSION_ID) + @Param(description = "ID of the extension that this custom action belongs to") + private String extensionId; + + @SerializedName(ApiConstants.EXTENSION_NAME) + @Param(description = "Name of the extension that this custom action belongs to") + private String extensionName; + + @SerializedName(ApiConstants.RESOURCE_TYPE) + @Param(description = "Resource type for which the action is available") + private String resourceType; + + @SerializedName(ApiConstants.ALLOWED_ROLE_TYPES) + @Param(description = "List of role types allowed for the custom action") + private List allowedRoleTypes; + + @SerializedName(ApiConstants.SUCCESS_MESSAGE) + @Param(description = "Message that will be used on successful execution of the action") + private String successMessage; + + @SerializedName(ApiConstants.ERROR_MESSAGE) + @Param(description = "Message that will be used on failure during execution of the action") + private String errorMessage; + + @SerializedName(ApiConstants.TIMEOUT) + @Param(description = "Specifies the timeout in seconds to wait for the action to complete before failing") + private Integer timeout; + + @SerializedName(ApiConstants.ENABLED) + @Param(description = "Whether the custom action is enabled or not") + private Boolean enabled; + + @SerializedName(ApiConstants.DETAILS) + @Param(description = "Details of the custom action") + private Map details; + + @SerializedName(ApiConstants.PARAMETERS) + @Param(description = "List of the parameters for the action", responseObject = ExtensionCustomActionParameterResponse.class) + private List parameters; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "Creation timestamp of the custom action") + private Date created; + + public ExtensionCustomActionResponse(String id, String name, String description) { + this.id = id; + this.name = name; + this.description = description; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public void setExtensionId(String extensionId) { + this.extensionId = extensionId; + } + + public void setExtensionName(String extensionName) { + this.extensionName = extensionName; + } + + public String getResourceType() { + return resourceType; + } + + public void setResourceType(String resourceType) { + this.resourceType = resourceType; + } + + public List getAllowedRoleTypes() { + return allowedRoleTypes; + } + + public void setAllowedRoleTypes(List allowedRoleTypes) { + this.allowedRoleTypes = allowedRoleTypes; + } + + public void setSuccessMessage(String successMessage) { + this.successMessage = successMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + public void setTimeout(Integer timeout) { + this.timeout = timeout; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + public void setParameters(List parameters) { + this.parameters = parameters; + } + + public List getParameters() { + return parameters; + } + + public void setDetails(Map details) { + this.details = details; + } + + public Map getDetails() { + return details; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ExtensionResourceResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ExtensionResourceResponse.java new file mode 100644 index 000000000000..aa370887b748 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/ExtensionResourceResponse.java @@ -0,0 +1,95 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.response; + +import org.apache.cloudstack.extension.ExtensionResourceMap; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; + +import java.util.Date; +import java.util.Map; + +@EntityReference(value = ExtensionResourceMap.class) +public class ExtensionResourceResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the resource associated with the extension") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "Name of the resource associated with this mapping") + private String name; + + @SerializedName(ApiConstants.TYPE) + @Param(description = "Type of the resource") + private String type; + + @SerializedName(ApiConstants.DETAILS) + @Param(description = "the details of the resource map") + private Map details; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "Creation timestamp of the mapping") + private Date created; + + public ExtensionResourceResponse() { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Map getDetails() { + return details; + } + + public void setDetails(Map details) { + this.details = details; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ExtensionResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ExtensionResponse.java new file mode 100644 index 000000000000..fdf1e87df506 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/ExtensionResponse.java @@ -0,0 +1,182 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.response; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.extension.Extension; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +@EntityReference(value = Extension.class) +public class ExtensionResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the extension") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "Name of the extension") + private String name; + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "Description of the extension") + private String description; + + @SerializedName(ApiConstants.TYPE) + @Param(description = "Type of the extension") + private String type; + + @SerializedName(ApiConstants.PATH) + @Param(description = "The path of the entry point fo the extension") + private String path; + + @SerializedName(ApiConstants.PATH_READY) + @Param(description = "True if the extension path is in ready state across management servers") + private Boolean pathReady; + + @SerializedName(ApiConstants.IS_USER_DEFINED) + @Param(description = "True if the extension is added by admin") + private Boolean userDefined; + + @SerializedName(ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM) + @Parameter(description = "Only honored when type is Orchestrator. Whether prepare VM is needed or not") + private Boolean orchestratorRequiresPrepareVm; + + @SerializedName(ApiConstants.STATE) + @Param(description = "The state of the extension") + private String state; + + @SerializedName(ApiConstants.DETAILS) + @Param(description = "The details of the extension") + private Map details; + + @SerializedName(ApiConstants.RESOURCES) + @Param(description = "List of resources to which extension is registered to", responseObject = ExtensionResourceResponse.class) + private List resources; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "Creation timestamp of the extension") + private Date created; + + @SerializedName(ApiConstants.REMOVED) + @Param(description = "Removal timestamp of the extension, if applicable") + private Date removed; + + public ExtensionResponse(String id, String name, String description, String type) { + this.id = id; + this.name = name; + this.description = description; + this.type = type; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public String getType() { + return type; + } + + public String getPath() { + return path; + } + + public Boolean isPathReady() { + return pathReady; + } + + public Boolean isUserDefined() { + return userDefined; + } + + public Boolean isOrchestratorRequiresPrepareVm() { + return orchestratorRequiresPrepareVm; + } + + public String getState() { + return state; + } + + public Map getDetails() { + return details; + } + + public void setPath(String path) { + this.path = path; + } + + public void setPathReady(Boolean pathReady) { + this.pathReady = pathReady; + } + + public void setUserDefined(Boolean userDefined) { + this.userDefined = userDefined; + } + + public void setOrchestratorRequiresPrepareVm(Boolean orchestratorRequiresPrepareVm) { + this.orchestratorRequiresPrepareVm = orchestratorRequiresPrepareVm; + } + + public void setState(String state) { + this.state = state; + } + + public void setDetails(Map details) { + this.details = details; + } + + public List getResources() { + return resources; + } + + public void setResources(List resources) { + this.resources = resources; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public Date getRemoved() { + return removed; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/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..5986c16dc8c0 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,63 @@ @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.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) { 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..48097e51d992 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,71 @@ @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.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() { 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 aac7e29a1734..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,31 +59,35 @@ 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) + @Param(description = "path of the domain to which the load balancer rule belongs", since = "4.19.2.0") + 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) { @@ -143,6 +147,11 @@ public void setDomainName(String domainName) { this.domainName = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } + public void setSiteLoadBalancers(List siteLoadBalancers) { this.siteLoadBalancers = siteLoadBalancers; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/GpuCardResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/GpuCardResponse.java new file mode 100644 index 000000000000..ad91b3490efa --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/GpuCardResponse.java @@ -0,0 +1,109 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.response; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.gpu.GpuCard; + +@EntityReference(value = GpuCard.class) +public class GpuCardResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) + @Param(description = "the ID of the GPU card") + protected String id; + + @SerializedName("deviceid") + @Param(description = "the device ID of the GPU card") + protected String deviceId; + + @SerializedName("devicename") + @Param(description = "the device name of the GPU card") + protected String deviceName; + + @SerializedName("name") + @Param(description = "the display name of the GPU card") + protected String name; + + @SerializedName("vendorname") + @Param(description = "the vendor name of the GPU card") + protected String vendorName; + + @SerializedName("vendorid") + @Param(description = "the vendor ID of the GPU card") + protected String vendorId; + + public GpuCardResponse(GpuCard gpuCard) { + super("gpucard"); + id = gpuCard.getUuid(); + deviceId = gpuCard.getDeviceId(); + deviceName = gpuCard.getDeviceName(); + name = gpuCard.getName(); + vendorName = gpuCard.getVendorName(); + vendorId = gpuCard.getVendorId(); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getDeviceName() { + return deviceName; + } + + public void setDeviceName(String deviceName) { + this.deviceName = deviceName; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getVendorName() { + return vendorName; + } + + public void setVendorName(String vendorName) { + this.vendorName = vendorName; + } + + public String getVendorId() { + return vendorId; + } + + public void setVendorId(String vendorId) { + this.vendorId = vendorId; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/GpuDeviceResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/GpuDeviceResponse.java new file mode 100644 index 000000000000..09e98b54eaa5 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/GpuDeviceResponse.java @@ -0,0 +1,227 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.response; + +import com.cloud.serializer.Param; +import com.cloud.vm.VirtualMachine; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.gpu.GpuDevice; + +@EntityReference(value = GpuDevice.class) +public class GpuDeviceResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "the ID of the GPU device") + private String id; + + @SerializedName(ApiConstants.BUS_ADDRESS) + @Param(description = "bus address of the GPU device or MDEV UUID for vGPU devices") + private String bussAddress; + + @SerializedName(ApiConstants.GPU_DEVICE_TYPE) + @Param(description = "bus address of the GPU device") + private GpuDevice.DeviceType type; + + @SerializedName(ApiConstants.HOST_ID) + @Param(description = "the host ID where the GPU device is attached") + private String hostId; + + @SerializedName(ApiConstants.HOST_NAME) + @Param(description = "the host name where the GPU device is attached") + private String hostName; + + @SerializedName(ApiConstants.GPU_CARD_ID) + @Param(description = "the GPU card ID associated with this GPU device") + private String gpuCardId; + + @SerializedName(ApiConstants.GPU_CARD_NAME) + @Param(description = "the GPU card name associated with this GPU device") + private String gpuCardName; + + @SerializedName(ApiConstants.VGPU_PROFILE_ID) + @Param(description = "the vGPU profile ID assigned to this GPU device") + private String vgpuProfileId; + + @SerializedName(ApiConstants.VGPU_PROFILE_NAME) + @Param(description = "the vGPU profile name assigned to this GPU device") + private String vgpuProfileName; + + @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) + @Param(description = "the vGPU profile ID assigned to this GPU device") + private String vmId; + + @SerializedName(ApiConstants.VIRTUAL_MACHINE_NAME) + @Param(description = "the vGPU profile name assigned to this GPU device") + private String vmName; + + @SerializedName(ApiConstants.VIRTUAL_MACHINE_STATE) + @Param(description = "the state of the virtual machine to which this GPU device is allocated") + private VirtualMachine.State vmState; + + @SerializedName(ApiConstants.STATE) + @Param(description = "the vGPU profile name assigned to this GPU device") + private GpuDevice.State state; + + @SerializedName(ApiConstants.MANAGED_STATE) + @Param(description = "the managed state of the GPU device (Enabled/Disabled)") + private GpuDevice.ManagedState managedState; + + @SerializedName(ApiConstants.PARENT_GPU_DEVICE_ID) + @Param(description = "the ID of the parent GPU device, if this is a vGPU") + private String parentGpuDeviceId; + + @SerializedName(ApiConstants.NUMA_NODE) + @Param(description = "the NUMA node where the GPU device is located") + private String numaNode; + + + public GpuDeviceResponse() { + // Empty constructor for serialization + super("gpudevice"); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getBussAddress() { + return bussAddress; + } + + public void setBussAddress(String bussAddress) { + this.bussAddress = bussAddress; + } + + public GpuDevice.DeviceType getType() { + return type; + } + + public void setType(GpuDevice.DeviceType type) { + this.type = type; + } + + public String getHostId() { + return hostId; + } + + public void setHostId(String hostId) { + this.hostId = hostId; + } + + public String getHostName() { + return hostName; + } + + public void setHostName(String hostName) { + this.hostName = hostName; + } + + public String getGpuCardId() { + return gpuCardId; + } + + public void setGpuCardId(String gpuCardId) { + this.gpuCardId = gpuCardId; + } + + public String getGpuCardName() { + return gpuCardName; + } + + public void setGpuCardName(String gpuCardName) { + this.gpuCardName = gpuCardName; + } + + public String getVgpuProfileId() { + return vgpuProfileId; + } + + public void setVgpuProfileId(String vgpuProfileId) { + this.vgpuProfileId = vgpuProfileId; + } + + public String getVgpuProfileName() { + return vgpuProfileName; + } + + public void setVgpuProfileName(String vgpuProfileName) { + this.vgpuProfileName = vgpuProfileName; + } + + public String getVmId() { + return vmId; + } + + public void setVmId(String vmId) { + this.vmId = vmId; + } + + public String getVmName() { + return vmName; + } + + public void setVmName(String vmName) { + this.vmName = vmName; + } + + public VirtualMachine.State getVmState() { + return vmState; + } + + public void setVmState(VirtualMachine.State vmState) { + this.vmState = vmState; + } + + public GpuDevice.State getState() { + return state; + } + + public void setState(GpuDevice.State state) { + this.state = state; + } + + public GpuDevice.ManagedState getManagedState() { + return managedState; + } + + public void setManagedState(GpuDevice.ManagedState managedState) { + this.managedState = managedState; + } + + public String getParentGpuDeviceId() { + return parentGpuDeviceId; + } + + public void setParentGpuDeviceId(String parentGpuDeviceId) { + this.parentGpuDeviceId = parentGpuDeviceId; + } + + public String getNumaNode() { + return numaNode; + } + + public void setNumaNode(String numaNode) { + this.numaNode = numaNode; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/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 7872bf220852..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 @@ -16,6 +16,8 @@ // under the License. package org.apache.cloudstack.api.response; +import java.util.Date; + import com.google.gson.annotations.SerializedName; import org.apache.cloudstack.api.ApiConstants; @@ -26,15 +28,27 @@ import com.cloud.storage.GuestOsCategory; @EntityReference(value = GuestOsCategory.class) -public class GuestOSCategoryResponse extends BaseResponse { +public class GuestOSCategoryResponse extends BaseResponse implements SetResourceIconResponse { @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the OS category") + @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) + @Param(description = "Whether the OS category is featured", since = "4.21.0") + private Boolean featured; + + @SerializedName(ApiConstants.RESOURCE_ICON) + @Param(description = "Base64 string representation of the resource icon", since = "4.21.0") + private ResourceIconResponse resourceIconResponse; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "Date when the OS category was created." ) + private Date created; + public String getId() { return id; } @@ -50,4 +64,17 @@ public String getName() { public void setName(String name) { this.name = name; } + + public void setFeatured(Boolean featured) { + this.featured = featured; + } + + @Override + public void setResourceIconResponse(ResourceIconResponse resourceIconResponse) { + this.resourceIconResponse = resourceIconResponse; + } + + public void setCreated(Date created) { + this.created = created; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/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 fab2a2cb4ce3..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,39 +29,43 @@ @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) + @Param(description = "path of the domain to which the guest VLAN range belongs", since = "4.19.2.0") + 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) { @@ -83,6 +87,10 @@ public void setDomainName(String domainName) { this.domainName = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } public void setGuestVlanRange(String guestVlanRange) { this.guestVlanRange = guestVlanRange; } 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 6bcc1d35cb15..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,63 +30,67 @@ 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) + @Param(description = "path of the domain to which the guest VLAN range belongs", since = "4.19.2.0") + 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) { @@ -108,6 +112,10 @@ 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; diff --git a/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java new file mode 100644 index 000000000000..fe8a85b4176e --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java @@ -0,0 +1,179 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//with the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +//KIND, either express or implied. See the License for the +//specific language governing permissions and limitations +//under the License. +package org.apache.cloudstack.api.response; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.gui.theme.GuiThemeJoin; + +import java.util.Date; + +@EntityReference(value = {GuiThemeJoin.class}) +public class GuiThemeResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the custom GUI theme.") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "Name of the GUI theme.") + private String name; + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "Description of the GUI theme.") + private String description; + + @SerializedName(ApiConstants.CSS) + @Param(description = "The CSS to be retrieved and imported into the GUI when matching the theme access configurations.") + private String css; + + @SerializedName(ApiConstants.JSON_CONFIGURATION) + @Param(description = "The JSON with the configurations to be retrieved and imported into the GUI when matching the theme access configurations.") + private String jsonConfiguration; + + @SerializedName(ApiConstants.COMMON_NAMES) + @Param(description = "A set of Common Names (CN) (fixed or wildcard) separated by comma that can retrieve the theme; e.g.: *acme.com,acme2.com") + private String commonNames; + + @SerializedName(ApiConstants.DOMAIN_IDS) + @Param(description = "A set of domain UUIDs (also known as ID for the end-user) separated by comma that can retrieve the theme.") + private String domainIds; + + @SerializedName(ApiConstants.RECURSIVE_DOMAINS) + @Param(description = "Whether to consider the subdomains of the informed domain IDs.") + private Boolean recursiveDomains; + + @SerializedName(ApiConstants.ACCOUNT_IDS) + @Param(description = "A set of account UUIDs (also known as ID for the end-user) separated by comma that can retrieve the theme.") + private String accountIds; + + @SerializedName(ApiConstants.IS_PUBLIC) + @Param(description = "Defines whether a theme can be retrieved by anyone when only the `commonNames` is informed. If the `domainIds` or `accountIds` is informed, it is " + + "considered as `false`.") + private Boolean isPublic; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "When the GUI theme was created.") + private Date created; + + @SerializedName(ApiConstants.REMOVED) + @Param(description = "When the GUI theme was removed.") + private Date removed; + + public GuiThemeResponse() { + super("guiThemes"); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getCss() { + return css; + } + + public void setCss(String css) { + this.css = css; + } + + public String getJsonConfiguration() { + return jsonConfiguration; + } + + public void setJsonConfiguration(String jsonConfiguration) { + this.jsonConfiguration = jsonConfiguration; + } + + public String getCommonNames() { + return commonNames; + } + + public void setCommonNames(String commonNames) { + this.commonNames = commonNames; + } + + public String getDomainIds() { + return domainIds; + } + + public void setDomainIds(String domainIds) { + this.domainIds = domainIds; + } + + public String getAccountIds() { + return accountIds; + } + + public void setAccountIds(String accountIds) { + this.accountIds = accountIds; + } + + public Boolean getPublic() { + return isPublic; + } + + public void setPublic(Boolean aPublic) { + isPublic = aPublic; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public Date getRemoved() { + return removed; + } + + public Boolean getRecursiveDomains() { + return recursiveDomains; + } + + public void setRecursiveDomains(Boolean recursiveDomains) { + this.recursiveDomains = recursiveDomains; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/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/HostForMigrationResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/HostForMigrationResponse.java index 41a0fdc4567e..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 @@ -16,449 +16,20 @@ // 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 com.cloud.host.Host; -import com.cloud.host.Status; import com.cloud.serializer.Param; import com.google.gson.annotations.SerializedName; @EntityReference(value = Host.class) -public class HostForMigrationResponse extends BaseResponse { - @SerializedName(ApiConstants.ID) - @Param(description = "the ID of the host") - private String id; - - @SerializedName(ApiConstants.NAME) - @Param(description = "the name of the host") - private String name; - - @SerializedName(ApiConstants.STATE) - @Param(description = "the state of the host") - private Status state; - - @SerializedName("disconnected") - @Param(description = "true if the host is disconnected. False otherwise.") - private Date disconnectedOn; - - @SerializedName(ApiConstants.TYPE) - @Param(description = "the host type") - private Host.Type hostType; - - @SerializedName("oscategoryid") - @Param(description = "the OS category ID of the host") - private String osCategoryId; - - @SerializedName("oscategoryname") - @Param(description = "the OS category name of the host") - private String osCategoryName; - - @SerializedName(ApiConstants.IP_ADDRESS) - @Param(description = "the IP address of the host") - private String ipAddress; - - @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "the Zone ID of the host") - private String zoneId; - - @SerializedName(ApiConstants.ZONE_NAME) - @Param(description = "the Zone name of the host") - private String zoneName; - - @SerializedName(ApiConstants.POD_ID) - @Param(description = "the Pod ID of the host") - private String podId; - - @SerializedName("podname") - @Param(description = "the Pod name of the host") - private String podName; - - @SerializedName("version") - @Param(description = "the host version") - private String version; - - @SerializedName(ApiConstants.HYPERVISOR) - @Param(description = "the host hypervisor") - private String hypervisor; - - @SerializedName("cpunumber") - @Param(description = "the CPU number of the host") - private Integer cpuNumber; - - @SerializedName("cpuspeed") - @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") - private String cpuAllocated; - - @SerializedName("cpuallocatedvalue") - @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") - private String cpuAllocatedPercentage; - - @SerializedName("cpuallocatedwithoverprovisioning") - @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") - private String cpuUsed; - - @SerializedName("cpuwithoverprovisioning") - @Param(description = "the amount of the host's CPU after applying the cpu.overprovisioning.factor ") - private String cpuWithOverprovisioning; - - @Deprecated - @SerializedName("memorytotal") - @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 ") - private String memWithOverprovisioning; - - @SerializedName("averageload") - @Param(description = "the cpu average load on the host") - private Long averageLoad; - - @SerializedName("networkkbsread") - @Param(description = "the incoming network traffic on the host") - private Long networkKbsRead; - - @SerializedName("networkkbswrite") - @Param(description = "the outgoing network traffic on the host") - private Long networkKbsWrite; - - @Deprecated - @SerializedName("memoryallocated") - @Param(description = "the amount of the host's memory currently allocated") - private String memoryAllocated; - - @SerializedName("memoryallocatedpercentage") - @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") - private Long memoryAllocatedBytes; - - @SerializedName("memoryused") - @Param(description = "the amount of the host's memory currently used") - private Long memoryUsed; - - @SerializedName("disksizetotal") - @Param(description = "the total disk size of the host") - private Long diskSizeTotal; - - @SerializedName("disksizeallocated") - @Param(description = "the host's currently allocated disk size") - private Long diskSizeAllocated; - - @SerializedName("capabilities") - @Param(description = "capabilities of the host") - private String capabilities; - - @SerializedName("lastpinged") - @Param(description = "the date and time the host was last pinged") - private Date lastPinged; - - @SerializedName("managementserverid") - @Param(description = "the management server ID of the host") - private Long managementServerId; - - @SerializedName("clusterid") - @Param(description = "the cluster ID of the host") - private String clusterId; - - @SerializedName("clustername") - @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") - private String clusterType; - - @SerializedName("islocalstorageactive") - @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") - private Date created; - - @SerializedName("removed") - @Param(description = "the date and time the host was removed") - private Date removed; - - @SerializedName("events") - @Param(description = "events available for the host") - private String events; - - @SerializedName("hosttags") - @Param(description = "comma-separated list of tags for the host") - private String hostTags; - - @SerializedName("hasenoughcapacity") - @Param(description = "true if this host has enough CPU and RAM capacity to migrate a VM 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") - private Boolean suitableForMigration; +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; - @SerializedName("resourcestate") - @Param(description = "the resource state of the host") - private String resourceState; - - @SerializedName(ApiConstants.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") - private Boolean haHost; - - @Override - public String getObjectId() { - return getId(); - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public void setName(String name) { - this.name = name; - } - - public void setState(Status state) { - this.state = state; - } - - public void setDisconnectedOn(Date disconnectedOn) { - this.disconnectedOn = disconnectedOn; - } - - public void setHostType(Host.Type hostType) { - this.hostType = hostType; - } - - public void setOsCategoryId(String osCategoryId) { - this.osCategoryId = osCategoryId; - } - - public void setOsCategoryName(String osCategoryName) { - this.osCategoryName = osCategoryName; - } - - public void setIpAddress(String ipAddress) { - this.ipAddress = ipAddress; - } - - public void setZoneId(String zoneId) { - this.zoneId = zoneId; - } - - public void setZoneName(String zoneName) { - this.zoneName = zoneName; - } - - public void setPodId(String podId) { - this.podId = podId; - } - - public void setPodName(String podName) { - this.podName = podName; - } - - public void setVersion(String version) { - this.version = version; - } - - public void setHypervisor(String hypervisor) { - this.hypervisor = hypervisor; - } - - public void setCpuNumber(Integer cpuNumber) { - this.cpuNumber = cpuNumber; - } - - public void setCpuSpeed(Long cpuSpeed) { - this.cpuSpeed = cpuSpeed; - } - - public String getCpuAllocated() { - return cpuAllocated; - } - - public void setCpuAllocated(String cpuAllocated) { - this.cpuAllocated = cpuAllocated; - } - - public void setCpuAllocatedValue(Long cpuAllocatedValue) { - this.cpuAllocatedValue = cpuAllocatedValue; - } - - public void setCpuAllocatedPercentage(String cpuAllocatedPercentage) { - this.cpuAllocatedPercentage = cpuAllocatedPercentage; - } - - public void setCpuAllocatedWithOverprovisioning(String cpuAllocatedWithOverprovisioning) { - this.cpuAllocatedWithOverprovisioning = cpuAllocatedWithOverprovisioning; - } - - public void setCpuUsed(String cpuUsed) { - this.cpuUsed = cpuUsed; - } - - public void setAverageLoad(Long averageLoad) { - this.averageLoad = averageLoad; - } - - public void setNetworkKbsRead(Long networkKbsRead) { - this.networkKbsRead = networkKbsRead; - } - - public void setNetworkKbsWrite(Long networkKbsWrite) { - this.networkKbsWrite = networkKbsWrite; - } - - public void setMemoryAllocated(String memoryAllocated) { - this.memoryAllocated = memoryAllocated; - } - - public void setMemoryAllocatedPercentage(String memoryAllocatedPercentage) { - this.memoryAllocatedPercentage = memoryAllocatedPercentage; - } - - public void setMemoryAllocatedBytes(Long memoryAllocatedBytes) { - this.memoryAllocatedBytes = memoryAllocatedBytes; - } - - public void setMemoryUsed(Long memoryUsed) { - this.memoryUsed = memoryUsed; - } - - public void setDiskSizeTotal(Long diskSizeTotal) { - this.diskSizeTotal = diskSizeTotal; - } - - public void setDiskSizeAllocated(Long diskSizeAllocated) { - this.diskSizeAllocated = diskSizeAllocated; - } - - public void setCapabilities(String capabilities) { - this.capabilities = capabilities; - } - - public void setLastPinged(Date lastPinged) { - this.lastPinged = lastPinged; - } - - public void setManagementServerId(Long managementServerId) { - this.managementServerId = managementServerId; - } - - public void setClusterId(String clusterId) { - this.clusterId = clusterId; - } - - public void setClusterName(String clusterName) { - this.clusterName = clusterName; - } - - public void setClusterType(String clusterType) { - this.clusterType = clusterType; - } - - public void setLocalStorageActive(Boolean localStorageActive) { - this.localStorageActive = localStorageActive; - } - - public void setCreated(Date created) { - this.created = created; - } - - public void setRemoved(Date removed) { - this.removed = removed; - } - - public void setEvents(String events) { - this.events = events; - } - - public String getHostTags() { - return hostTags; - } - - public void setHostTags(String hostTags) { - this.hostTags = hostTags; - } - - public void setHasEnoughCapacity(Boolean hasEnoughCapacity) { - this.hasEnoughCapacity = hasEnoughCapacity; - } - - public void setSuitableForMigration(Boolean suitableForMigration) { - this.suitableForMigration = suitableForMigration; - } - public void setRequiresStorageMotion(Boolean requiresStorageMotion) { this.requiresStorageMotion = requiresStorageMotion; } - - public String getResourceState() { - return resourceState; - } - - public void setResourceState(String resourceState) { - this.resourceState = resourceState; - } - - public String getCpuWithOverprovisioning() { - return cpuWithOverprovisioning; - } - - public void setCpuWithOverprovisioning(String cpuWithOverprovisioning) { - this.cpuWithOverprovisioning = cpuWithOverprovisioning; - } - - public void setMemWithOverprovisioning(String memWithOverprovisioning){ - this.memWithOverprovisioning=memWithOverprovisioning; - } - - public void setHypervisorVersion(String hypervisorVersion) { - this.hypervisorVersion = hypervisorVersion; - } - - public Boolean getHaHost() { - return haHost; - } - - public void setHaHost(Boolean haHost) { - this.haHost = haHost; - } - - public void setMemoryTotal(Long memoryTotal) { - this.memoryTotal = memoryTotal; - } } 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 d72d23b99c9c..5d085d1bee05 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 @@ -29,228 +29,254 @@ import com.cloud.host.Host; import com.cloud.host.Status; +import com.cloud.hypervisor.Hypervisor; import com.cloud.serializer.Param; import com.google.gson.annotations.SerializedName; @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.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") + @Param(description = "Total GPUs on the Host", responseObject = Long.class, since = "4.21") + private Long gpuTotal; + + @SerializedName("gpuused") + @Param(description = "Used GPUs on the Host", responseObject = Long.class, since = "4.21") + private Long gpuUsed; + @SerializedName(ApiConstants.GPUGROUP) @Param(description = "GPU cards present in the host", responseObject = GpuResponse.class, since = "4.4") private List gpuGroup; @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("managementserverid") - @Param(description = "the management server ID of the host") + @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(ApiConstants.MANAGEMENT_SERVER_ID) + @Param(description = "The management server ID of the host") private String managementServerId; + @SerializedName(ApiConstants.MANAGEMENT_SERVER_NAME) + @Param(description = "the management server name of the host", since = "4.21.0") + private String managementServerName; + + private transient long clusterInternalId; + @SerializedName("clusterid") - @Param(description = "the cluster ID of the host") + @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") + @Param(description = "comma-separated list of explicit host tags for the host", since = "4.20.0") + private String explicitHostTags; + + @SerializedName("implicithosttags") + @Param(description = "comma-separated list of implicit host tags for the host", since = "4.20.0") + private String implicitHostTags; + @SerializedName(ApiConstants.IS_TAG_A_RULE) @Param(description = ApiConstants.PARAMETER_DESCRIPTION_IS_TAG_A_RULE) 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) @@ -258,25 +284,57 @@ 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") - private Boolean uefiCapabilty; + @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) + @Param(description = "true if the host supports instance conversion (using virt-v2v)", since = "4.19.1") + private Boolean instanceConversionSupported; + + @SerializedName(ApiConstants.ARCH) + @Param(description = "CPU Arch of the host", since = "4.20") + private String arch; + + @SerializedName(ApiConstants.STORAGE_ACCESS_GROUPS) + @Param(description = "comma-separated list of storage access groups for the host", since = "4.21.0") + private String storageAccessGroups; + + @SerializedName(ApiConstants.CLUSTER_STORAGE_ACCESS_GROUPS) + @Param(description = "comma-separated list of storage access groups on the cluster", since = "4.21.0") + private String clusterStorageAccessGroups; + + @SerializedName(ApiConstants.POD_STORAGE_ACCESS_GROUPS) + @Param(description = "comma-separated list of storage access groups on the pod", since = "4.21.0") + private String podStorageAccessGroups; + + @SerializedName(ApiConstants.ZONE_STORAGE_ACCESS_GROUPS) + @Param(description = "comma-separated list of storage access groups on the zone", since = "4.21.0") + private String zoneStorageAccessGroups; + + @SerializedName(ApiConstants.EXTENSION_ID) + @Param(description="The ID of extension for this cluster", since = "4.21.0") + private String extensionId; + + @SerializedName(ApiConstants.EXTENSION_NAME) + @Param(description="The name of extension for this cluster", since = "4.21.0") + private String extensionName; + @Override public String getObjectId() { return this.getId(); @@ -398,6 +456,14 @@ public void setMemoryUsed(Long memoryUsed) { this.memoryUsed = memoryUsed; } + public void setGpuTotal(Long gpuTotal) { + this.gpuTotal = gpuTotal; + } + + public void setGpuUsed(Long gpuUsed) { + this.gpuUsed = gpuUsed; + } + public void setGpuGroup(List gpuGroup) { this.gpuGroup = gpuGroup; } @@ -418,10 +484,26 @@ public void setLastPinged(Date lastPinged) { this.lastPinged = lastPinged; } + public void setVirtualMachineId(String virtualMachineId) { + this.virtualMachineId = virtualMachineId; + } + public void setManagementServerId(String managementServerId) { this.managementServerId = managementServerId; } + public void setManagementServerName(String managementServerName) { + this.managementServerName = managementServerName; + } + + public long getClusterInternalId() { + return clusterInternalId; + } + + public void setClusterInternalId(long clusterInternalId) { + this.clusterInternalId = clusterInternalId; + } + public void setClusterId(String clusterId) { this.clusterId = clusterId; } @@ -458,6 +540,54 @@ public void setHostTags(String hostTags) { this.hostTags = hostTags; } + public String getStorageAccessGroups() { + return storageAccessGroups; + } + + public void setStorageAccessGroups(String storageAccessGroups) { + this.storageAccessGroups = storageAccessGroups; + } + + public String getClusterStorageAccessGroups() { + return clusterStorageAccessGroups; + } + + public void setClusterStorageAccessGroups(String clusterStorageAccessGroups) { + this.clusterStorageAccessGroups = clusterStorageAccessGroups; + } + + public String getPodStorageAccessGroups() { + return podStorageAccessGroups; + } + + public void setPodStorageAccessGroups(String podStorageAccessGroups) { + this.podStorageAccessGroups = podStorageAccessGroups; + } + + public String getZoneStorageAccessGroups() { + return zoneStorageAccessGroups; + } + + public void setZoneStorageAccessGroups(String zoneStorageAccessGroups) { + this.zoneStorageAccessGroups = zoneStorageAccessGroups; + } + + public String getExplicitHostTags() { + return explicitHostTags; + } + + public void setExplicitHostTags(String explicitHostTags) { + this.explicitHostTags = explicitHostTags; + } + + public String getImplicitHostTags() { + return implicitHostTags; + } + + public void setImplicitHostTags(String implicitHostTags) { + this.implicitHostTags = implicitHostTags; + } + public void setHasEnoughCapacity(Boolean hasEnoughCapacity) { this.hasEnoughCapacity = hasEnoughCapacity; } @@ -526,7 +656,7 @@ public void setUsername(String username) { this.username = username; } - public void setDetails(Map details) { + public void setDetails(Map details, Hypervisor.HypervisorType hypervisorType) { if (details == null) { return; @@ -547,6 +677,15 @@ public void setDetails(Map details) { this.setEncryptionSupported(new Boolean(false)); // default } + if (Hypervisor.HypervisorType.KVM.equals(hypervisorType)) { + if (detailsCopy.containsKey(Host.HOST_INSTANCE_CONVERSION)) { + this.setInstanceConversionSupported(Boolean.parseBoolean((String) detailsCopy.get(Host.HOST_INSTANCE_CONVERSION))); + detailsCopy.remove(Host.HOST_INSTANCE_CONVERSION); + } else { + this.setInstanceConversionSupported(new Boolean(false)); // default + } + } + this.details = detailsCopy; } @@ -681,10 +820,18 @@ public Date getLastPinged() { return lastPinged; } + public String getVirtualMachineId() { + return virtualMachineId; + } + public String getManagementServerId() { return managementServerId; } + public String getManagementServerName() { + return managementServerName; + } + public String getClusterId() { return clusterId; } @@ -697,7 +844,7 @@ public String getClusterType() { return clusterType; } - public Boolean isLocalStorageActive() { + public Boolean getLocalStorageActive() { return localStorageActive; } @@ -717,7 +864,7 @@ public Boolean hasEnoughCapacity() { return hasEnoughCapacity; } - public Boolean isSuitableForMigration() { + public Boolean getSuitableForMigration() { return suitableForMigration; } @@ -729,14 +876,18 @@ public Boolean getHaHost() { return haHost; } - public void setUefiCapabilty(Boolean hostCapability) { - this.uefiCapabilty = hostCapability; + public void setUefiCapability(Boolean hostCapability) { + this.uefiCapability = hostCapability; } public void setEncryptionSupported(Boolean encryptionSupported) { this.encryptionSupported = encryptionSupported; } + public void setInstanceConversionSupported(Boolean instanceConversionSupported) { + this.instanceConversionSupported = instanceConversionSupported; + } + public Boolean getIsTagARule() { return isTagARule; } @@ -744,4 +895,108 @@ public Boolean getIsTagARule() { public void setIsTagARule(Boolean tagARule) { isTagARule = tagARule; } + + public void setArch(String arch) { + this.arch = arch; + } + + public String getArch() { + return arch; + } + + public Long getCpuAllocatedValue() { + return cpuAllocatedValue; + } + + public String getCpuAllocatedPercentage() { + return cpuAllocatedPercentage; + } + + public String getCpuAllocatedWithOverprovisioning() { + return cpuAllocatedWithOverprovisioning; + } + + public Double getCpuloadaverage() { + return cpuloadaverage; + } + + public void setCpuloadaverage(Double cpuloadaverage) { + this.cpuloadaverage = cpuloadaverage; + } + + public String getMemWithOverprovisioning() { + return memWithOverprovisioning; + } + + public String getMemoryAllocatedPercentage() { + return memoryAllocatedPercentage; + } + + public Long getMemoryAllocatedBytes() { + return memoryAllocatedBytes; + } + + public Long getGpuTotal() { + return gpuTotal; + } + + public Long getGpuUsed() { + return gpuUsed; + } + + public Boolean getTagARule() { + return isTagARule; + } + + public void setTagARule(Boolean tagARule) { + isTagARule = tagARule; + } + + public Boolean getHasEnoughCapacity() { + return hasEnoughCapacity; + } + + public void setDetails(Map details) { + this.details = details; + } + + public String getAnnotation() { + return annotation; + } + + public Date getLastAnnotated() { + return lastAnnotated; + } + + public String getUsername() { + return username; + } + + public Boolean getUefiCapability() { + return uefiCapability; + } + + public Boolean getEncryptionSupported() { + return encryptionSupported; + } + + public Boolean getInstanceConversionSupported() { + return instanceConversionSupported; + } + + public void setExtensionId(String extensionId) { + this.extensionId = extensionId; + } + + public String getExtensionId() { + return extensionId; + } + + public void setExtensionName(String extensionName) { + this.extensionName = extensionName; + } + + public String getExtensionName() { + return extensionName; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/HostTagResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/HostTagResponse.java index 4a924ea78a08..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 @@ -19,21 +19,26 @@ import com.google.gson.annotations.SerializedName; import com.cloud.serializer.Param; +import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; 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) + @Param(description = "true if the host tag is implicit", since = "4.20.0") + private boolean isImplicit; + public String getId() { return id; } @@ -57,4 +62,12 @@ public String getName() { public void setName(String name) { this.name = name; } + + public boolean isImplicit() { + return isImplicit; + } + + public void setImplicit(boolean implicit) { + isImplicit = 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 e2bf6ef5c60f..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,141 +32,153 @@ @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) + @Param(description = "path of the domain to which the public IP address belongs", since = "4.19.2.0") + 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) + @Param(description="true if range is dedicated for System VMs") + private boolean forSystemVms; + + @SerializedName(ApiConstants.FOR_PROVIDER) + @Param(description="true if range is dedicated for external network providers", since = "4.21.0") + private boolean forProvider; + public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; } @@ -207,6 +219,10 @@ public void setDomainName(String domainName) { this.domainName = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } public void setForVirtualNetwork(Boolean forVirtualNetwork) { this.forVirtualNetwork = forVirtualNetwork; } @@ -316,4 +332,12 @@ public void setNetworkName(String networkName) { public void setHasRules(final boolean hasRules) { this.hasRules = hasRules; } + + public void setForSystemVms(boolean forSystemVms) { + this.forSystemVms = forSystemVms; + } + + public void setForProvider(boolean forProvider) { + this.forProvider = forProvider; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/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 532963dbddc0..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 @@ -27,48 +27,48 @@ @EntityReference(value = ImageStore.class) public class ImageStoreResponse extends BaseResponseWithAnnotations { - @SerializedName("id") - @Param(description = "the ID of the image store") + @SerializedName(ApiConstants.ID) + @Param(description = "The ID of the image store") private String id; - @SerializedName("zoneid") - @Param(description = "the Zone ID of the image store") + @SerializedName(ApiConstants.ZONE_ID) + @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("name") - @Param(description = "the name of the image store") + @SerializedName(ApiConstants.NAME) + @Param(description = "The name of the image store") private String name; - @SerializedName("url") - @Param(description = "the url of the image store") + @SerializedName(ApiConstants.URL) + @Param(description = "The URL of the image store") private String url; - @SerializedName("protocol") - @Param(description = "the protocol of the image store") + @SerializedName(ApiConstants.PROTOCOL) + @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("scope") - @Param(description = "the scope of the image store") + @SerializedName(ApiConstants.SCOPE) + @Param(description = "The scope of the image store") private ScopeType scope; - @SerializedName("readonly") - @Param(description = "defines if store is read-only") + @SerializedName(ApiConstants.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/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 e1241cc19bc7..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,37 +32,41 @@ 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) + @Param(description = "path of the Domain the instance group belongs to", since = "4.19.2.0") + private String domainPath; + public void setId(String id) { this.id = id; } @@ -90,6 +94,11 @@ 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; 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/Ipv4RouteResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/Ipv4RouteResponse.java new file mode 100644 index 000000000000..136c87971b79 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/Ipv4RouteResponse.java @@ -0,0 +1,59 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.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 Ipv4RouteResponse extends BaseResponse { + + @SerializedName(ApiConstants.SUBNET) + @Param(description = "the guest Ipv4 cidr for route") + private String subnet; + + @SerializedName(ApiConstants.GATEWAY) + @Param(description = "the outbound Ipv4 gateway") + private String gateway; + + public Ipv4RouteResponse() { + } + + public Ipv4RouteResponse(String subnet, String gateway) { + this.subnet = subnet; + this.gateway = gateway; + } + + public String getSubnet() { + return subnet; + } + + public void setSubnet(String subnet) { + this.subnet = subnet; + } + + public String getGateway() { + return gateway; + } + + public void setGateway(String gateway) { + this.gateway = gateway; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/Ipv4SubnetForGuestNetworkResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/Ipv4SubnetForGuestNetworkResponse.java new file mode 100644 index 000000000000..1430bcd059c1 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/Ipv4SubnetForGuestNetworkResponse.java @@ -0,0 +1,199 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.network.Ipv4GuestSubnetNetworkMap; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +@EntityReference(value = Ipv4GuestSubnetNetworkMap.class) +public class Ipv4SubnetForGuestNetworkResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) + @Param(description = "id of the IPv4 subnet for guest network") + private String id; + + @SerializedName(ApiConstants.PARENT_ID) + @Param(description = "id of the data center IPv4 subnet") + private String parentId; + + @SerializedName(ApiConstants.PARENT_SUBNET) + @Param(description = "subnet of the data center IPv4 subnet") + private String parentSubnet; + + @SerializedName(ApiConstants.SUBNET) + @Param(description = "subnet of the IPv4 network") + private String subnet; + + @SerializedName(ApiConstants.STATE) + @Param(description = "state of subnet of the IPv4 network") + private String state; + + @SerializedName(ApiConstants.ZONE_ID) + @Param(description = "id of zone to which the IPv4 subnet belongs to." ) + private String zoneId; + + @SerializedName(ApiConstants.ZONE_NAME) + @Param(description = "id of zone to which the IPv4 subnet belongs to." ) + private String zoneName; + + @SerializedName(ApiConstants.NETWORK_ID) + @Param(description = "id of network which the IPv4 subnet is associated with." ) + private String networkId; + + @SerializedName(ApiConstants.NETWORK_NAME) + @Param(description = "name of network which the IPv4 subnet is associated with." ) + private String networkName; + + @SerializedName(ApiConstants.VPC_ID) + @Param(description = "Id of the VPC which the IPv4 subnet is associated with.") + private String vpcId; + + @SerializedName(ApiConstants.VPC_NAME) + @Param(description = "Name of the VPC which the IPv4 subnet is associated with.") + private String vpcName; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "date when this IPv4 subnet was created." ) + private Date created; + + @SerializedName(ApiConstants.REMOVED) + @Param(description = "date when this IPv4 subnet was removed." ) + private Date removed; + + @SerializedName(ApiConstants.ALLOCATED_TIME) + @Param(description = "date when this IPv4 subnet was allocated." ) + private Date allocatedTime; + + public void setId(String id) { + this.id = id; + } + + public void setParentId(String parentId) { + this.parentId = parentId; + } + + public void setParentSubnet(String parentSubnet) { + this.parentSubnet = parentSubnet; + } + + public void setSubnet(String subnet) { + this.subnet = subnet; + } + + public void setState(String state) { + this.state = state; + } + + public void setNetworkId(String networkId) { + this.networkId = networkId; + } + + public void setNetworkName(String networkName) { + this.networkName = networkName; + } + + public void setVpcId(String vpcId) { + this.vpcId = vpcId; + } + + public void setVpcName(String vpcName) { + this.vpcName = vpcName; + } + + public void setZoneId(String zoneId) { + this.zoneId = zoneId; + } + + public void setZoneName(String zoneName) { + this.zoneName = zoneName; + } + + public void setCreated(Date created) { + this.created = created; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } + + public void setAllocatedTime(Date allocatedTime) { + this.allocatedTime = allocatedTime; + } + + public String getId() { + return id; + } + + public String getParentId() { + return parentId; + } + + public String getParentSubnet() { + return parentSubnet; + } + + public String getSubnet() { + return subnet; + } + + public String getState() { + return state; + } + + public String getZoneId() { + return zoneId; + } + + public String getZoneName() { + return zoneName; + } + + public String getNetworkId() { + return networkId; + } + + public String getNetworkName() { + return networkName; + } + + public String getVpcId() { + return vpcId; + } + + public String getVpcName() { + return vpcName; + } + + public Date getCreated() { + return created; + } + + public Date getRemoved() { + return removed; + } + + public Date getAllocatedTime() { + return allocatedTime; + } +} 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/KubernetesUserVmResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/KubernetesUserVmResponse.java new file mode 100644 index 000000000000..cef5cdae2f45 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/KubernetesUserVmResponse.java @@ -0,0 +1,51 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.response; + +import com.cloud.network.router.VirtualRouter; +import com.cloud.serializer.Param; +import com.cloud.uservm.UserVm; +import com.cloud.vm.VirtualMachine; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.EntityReference; + +@EntityReference(value = {VirtualMachine.class, UserVm.class, VirtualRouter.class}) +public class KubernetesUserVmResponse extends UserVmResponse { + @SerializedName(ApiConstants.IS_EXTERNAL_NODE) + @Param(description = "If the VM is an externally added node") + private boolean isExternalNode; + + @SerializedName(ApiConstants.IS_ETCD_NODE) + @Param(description = "If the VM is an etcd node") + private boolean isEtcdNode; + + @SerializedName(ApiConstants.KUBERNETES_NODE_VERSION) + @Param(description = "Kubernetes version of the node") + private String nodeVersion; + + + public void setExternalNode(boolean externalNode) { + isExternalNode = externalNode; + } + + public void setEtcdNode(boolean etcdNode) { + isEtcdNode = etcdNode; + } + + public void setNodeVersion(String nodeVersion) { this.nodeVersion = nodeVersion;} +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/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 327482695875..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,87 +28,91 @@ @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) + @Param(description = "path of the domain to which the load balancer rule 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.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) { @@ -158,6 +162,11 @@ public void setDomainName(String domainName) { this.domainName = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } + public void setState(String state) { this.state = state; } 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 84c79d323218..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) @@ -86,6 +86,14 @@ public class LoginCmdResponse extends AuthenticationCmdResponse { @Param(description = "Two factor authentication issuer", since = "4.18.0.0") private String issuerFor2FA; + @SerializedName(value = ApiConstants.MANAGEMENT_SERVER_ID) + @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; } @@ -211,4 +219,20 @@ public String getIssuerFor2FA() { public void setIssuerFor2FA(String issuerFor2FA) { this.issuerFor2FA = issuerFor2FA; } + + public String getManagementServerId() { + return managementServerId; + } + + 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 330f91e69f31..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 @@ -24,58 +24,85 @@ import org.apache.cloudstack.api.EntityReference; import org.apache.cloudstack.management.ManagementServerHost.State; +import java.util.ArrayList; import java.util.Date; +import java.util.List; @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") + @Param(description = "The IP Address for this Management Server. This is deprecated, please use 'ipaddress' instead.") private String serviceIp; + @SerializedName(ApiConstants.IP_ADDRESS) + @Param(description = "the IP Address for this Management Server") + private String ipAddress; + + @SerializedName(ApiConstants.PEERS) + @Param(description = "the Management Server Peers") + private List peers; + + @SerializedName(ApiConstants.LAST_AGENTS) + @Param(description = "the last agents this Management Server is responsible for, before shutdown or preparing for maintenance", since = "4.21.0.0") + private List lastAgents; + + @SerializedName(ApiConstants.AGENTS) + @Param(description = "the agents this Management Server is responsible for", since = "4.21.0.0") + private List agents; + + @SerializedName(ApiConstants.AGENTS_COUNT) + @Param(description = "the number of host agents this Management Server is responsible for", since = "4.21.0.0") + private Long agentsCount; + + @SerializedName(ApiConstants.PENDING_JOBS_COUNT) + @Param(description = "the number of pending jobs in this Management Server", since = "4.21.0.0") + private Long pendingJobsCount; + public String getId() { return this.id; } @@ -120,6 +147,26 @@ public String getServiceIp() { return serviceIp; } + public String getIpAddress() { + return ipAddress; + } + + public List getLastAgents() { + return lastAgents; + } + + public List getAgents() { + return agents; + } + + public Long getAgentsCount() { + return this.agentsCount; + } + + public Long getPendingJobsCount() { + return this.pendingJobsCount; + } + public void setId(String id) { this.id = id; } @@ -167,4 +214,43 @@ public void setKernelVersion(String kernelVersion) { public void setServiceIp(String serviceIp) { this.serviceIp = serviceIp; } + + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } + + public void setLastAgents(List lastAgents) { + this.lastAgents = lastAgents; + } + + public void setAgents(List agents) { + this.agents = agents; + } + + public void setAgentsCount(Long agentsCount) { + this.agentsCount = agentsCount; + } + + public void setPendingJobsCount(Long pendingJobsCount) { + this.pendingJobsCount = pendingJobsCount; + } + + public String getKernelVersion() { + return kernelVersion; + } + + public List getPeers() { + return peers; + } + + public void setPeers(List peers) { + this.peers = peers; + } + + public void addPeer(PeerManagementServerNodeResponse peer) { + if (peers == null) { + peers = new ArrayList<>(); + } + peers.add(peer); + } } 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 b92725d883e4..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,121 +32,141 @@ @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) + @Param(description = "true if network offering can be used by NSX networks only") + 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) + @Param(description = "Mode in which the network will operate. The valid values are NATTED and ROUTED") + 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) + @Param(description = "true if network offering supports public access for guest networks", since = "4.20.0") + 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) + @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.") + private String routingMode; + public void setId(String id) { this.id = id; } @@ -215,10 +235,18 @@ public void setForVpc(Boolean forVpc) { this.forVpc = forVpc; } + public void setForNsx(Boolean forNsx) { + this.forNsx = forNsx; + } + public void setForTungsten(Boolean forTungsten) { this.forTungsten = forTungsten; } + public void setNetworkMode(String networkMode) { + this.networkMode = networkMode; + } + public void setIsPersistent(Boolean isPersistent) { this.isPersistent = isPersistent; } @@ -243,6 +271,10 @@ public void setSupportsPublicAccess(Boolean supportsPublicAccess) { this.supportsPublicAccess = supportsPublicAccess; } + public void setSupportsInternalLb(Boolean supportsInternalLb) { + this.supportsInternalLb = supportsInternalLb; + } + public String getDomainId() { return domainId; } @@ -282,4 +314,20 @@ public String getInternetProtocol() { public void setInternetProtocol(String internetProtocol) { this.internetProtocol = internetProtocol; } + + public Boolean getSpecifyAsNumber() { + return specifyAsNumber; + } + + public void setSpecifyAsNumber(Boolean specifyAsNumber) { + this.specifyAsNumber = specifyAsNumber; + } + + public String getRoutingMode() { + return routingMode; + } + + public void setRoutingMode(String routingMode) { + this.routingMode = routingMode; + } } 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 d34f949372ae..7c4b733a80f5 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 @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.response; import java.util.Date; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -27,244 +28,247 @@ import org.apache.cloudstack.api.EntityReference; import com.cloud.network.Network; -import com.cloud.projects.ProjectAccount; import com.cloud.serializer.Param; import com.google.gson.annotations.SerializedName; @SuppressWarnings("unused") -@EntityReference(value = {Network.class, ProjectAccount.class}) +@EntityReference(value = {Network.class}) public class NetworkResponse extends BaseResponseWithAssociatedNetwork implements ControlledEntityResponse, SetResourceIconResponse { @SerializedName(ApiConstants.ID) - @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) + @Param(description = "UUID of AS NUMBER", since = "4.20.0") + private String asNumberId; + + @SerializedName(ApiConstants.AS_NUMBER) + @Param(description = "AS NUMBER", since = "4.20.0") + 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") + 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.ASSOCIATED_NETWORK_ID) - @Param(description = "the ID of the Network associated with this network") - private String associatedNetworkId; - - @SerializedName(ApiConstants.ASSOCIATED_NETWORK) - @Param(description = "the name of the Network associated with this network") - private String associatedNetworkName; - @SerializedName(ApiConstants.TUNGSTEN_VIRTUAL_ROUTER_UUID) - @Param(description = "Tungsten-Fabric virtual router the network belongs to") + @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) @@ -272,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 routing mode 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) @@ -308,13 +312,25 @@ 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) + @Param(description = "The IPv4 routing type of network", since = "4.20.0") + private String ipv4Routing; + + @SerializedName(ApiConstants.IPV4_ROUTES) + @Param(description = "The routes for the network to ease adding route in upstream router", since = "4.20.0") + private Set ipv4Routes; + + @SerializedName(ApiConstants.BGP_PEERS) + @Param(description = "The BGP peers for the network", since = "4.20.0") + private Set bgpPeers; + public NetworkResponse() {} public Boolean getDisplayNetwork() { @@ -415,6 +431,14 @@ public void setVlan(String vlan) { this.vlan = vlan; } + public void setAsNumber(long asNumber) { + this.asNumber = asNumber; + } + + public void setAsNumberId(String asNumberId) { + this.asNumberId = asNumberId; + } + public void setIsSystem(Boolean isSystem) { this.isSystem = isSystem; } @@ -424,6 +448,7 @@ public void setDomainName(String domain) { this.domain = domain; } + @Override public void setDomainPath(String domainPath) { this.domainPath = domainPath; } @@ -486,6 +511,10 @@ public void setRestartRequired(Boolean restartRequired) { this.restartRequired = restartRequired; } + public void setSpecifyVlan(Boolean specifyVlan) { + this.specifyVlan = specifyVlan; + } + public void setSpecifyIpRanges(Boolean specifyIpRanges) { this.specifyIpRanges = specifyIpRanges; } @@ -582,14 +611,6 @@ public void setVpcName(String vpcName) { this.vpcName = vpcName; } - public void setAssociatedNetworkId(String associatedNetworkId) { - this.associatedNetworkId = associatedNetworkId; - } - - public void setAssociatedNetworkName(String associatedNetworkName) { - this.associatedNetworkName = associatedNetworkName; - } - @Override public void setResourceIconResponse(ResourceIconResponse icon) { this.icon = icon; @@ -623,6 +644,18 @@ public void setInternetProtocol(String internetProtocol) { this.internetProtocol = internetProtocol; } + public void setIpv4Routing(String ipv4Routing) { + this.ipv4Routing = ipv4Routing; + } + + public void setIpv4Routes(Set ipv4Routes) { + this.ipv4Routes = ipv4Routes; + } + + public void addIpv4Route(Ipv4RouteResponse ipv4Route) { + this.ipv4Routes.add(ipv4Route); + } + public void setIpv6Routing(String ipv6Routing) { this.ipv6Routing = ipv6Routing; } @@ -635,6 +668,17 @@ public void addIpv6Route(Ipv6RouteResponse ipv6Route) { this.ipv6Routes.add(ipv6Route); } + public void setBgpPeers(Set bgpPeers) { + this.bgpPeers = bgpPeers; + } + + public void addBgpPeer(BgpPeerResponse bgpPeer) { + if (this.bgpPeers == null) { + this.setBgpPeers(new LinkedHashSet<>()); + } + this.bgpPeers.add(bgpPeer); + } + public Integer getPublicMtu() { return publicMtu; } 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..f992514b8db2 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,11 +139,11 @@ 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; public void setVmId(String vmId) { @@ -223,11 +223,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 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..eba00da25cfe 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,15 +42,15 @@ 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; @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ObjectStoreResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ObjectStoreResponse.java index e4030799aa79..dcb93aaaf1d2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ObjectStoreResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ObjectStoreResponse.java @@ -17,6 +17,8 @@ package org.apache.cloudstack.api.response; import com.cloud.serializer.Param; + +import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.storage.object.ObjectStore; import com.google.gson.annotations.SerializedName; import org.apache.cloudstack.api.BaseResponseWithAnnotations; @@ -24,15 +26,15 @@ @EntityReference(value = ObjectStore.class) public class ObjectStoreResponse extends BaseResponseWithAnnotations { - @SerializedName("id") + @SerializedName(ApiConstants.ID) @Param(description = "the ID of the object store") private String id; - @SerializedName("name") + @SerializedName(ApiConstants.NAME) @Param(description = "the name of the object store") private String name; - @SerializedName("url") + @SerializedName(ApiConstants.URL) @Param(description = "the url of the object store") private String url; @@ -44,6 +46,10 @@ public class ObjectStoreResponse extends BaseResponseWithAnnotations { @Param(description = "the total size of the object store") private Long storageTotal; + @SerializedName("storageallocated") + @Param(description = "the allocated size of the object store") + private Long storageAllocated; + @SerializedName("storageused") @Param(description = "the object store currently used size") private Long storageUsed; @@ -96,6 +102,14 @@ public void setStorageTotal(Long storageTotal) { this.storageTotal = storageTotal; } + public Long getStorageAllocated() { + return storageAllocated; + } + + public void setStorageAllocated(Long storageAllocated) { + this.storageAllocated = storageAllocated; + } + public Long getStorageUsed() { return storageUsed; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/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 ac5b9e309c8c..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,31 +29,35 @@ 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") + private String domainPath; + @Override public void setAccountName(String accountName) { this.accountName = accountName; @@ -73,6 +77,10 @@ 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; diff --git a/api/src/main/java/org/apache/cloudstack/api/response/PeerManagementServerNodeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/PeerManagementServerNodeResponse.java new file mode 100644 index 000000000000..802294171faf --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/PeerManagementServerNodeResponse.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.response; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.management.ManagementServerHost.State; + +import java.util.Date; + +public class PeerManagementServerNodeResponse extends BaseResponse { + + @SerializedName(ApiConstants.STATE) + @Param(description = "the state of the management server peer") + private State state; + + @SerializedName(ApiConstants.LAST_UPDATED) + @Param(description = "the last updated time of the management server peer state") + private Date lastUpdated; + + @SerializedName(ApiConstants.PEER_ID) + @Param(description = "the ID of the peer management server") + private String peerId; + + @SerializedName(ApiConstants.PEER_NAME) + @Param(description = "the name of the peer management server") + private String peerName; + + @SerializedName(ApiConstants.PEER_MSID) + @Param(description = "the management ID of the peer management server") + private String peerMsId; + + @SerializedName(ApiConstants.PEER_RUNID) + @Param(description = "the run ID of the peer management server") + private String peerRunId; + + @SerializedName(ApiConstants.PEER_STATE) + @Param(description = "the state of the peer management server") + private String peerState; + + @SerializedName(ApiConstants.PEER_SERVICE_IP) + @Param(description = "the IP Address for the peer Management Server") + private String peerServiceIp; + + @SerializedName(ApiConstants.PEER_SERVICE_PORT) + @Param(description = "the service port for the peer Management Server") + private String peerServicePort; + + public void setState(State state) { + this.state = state; + } + + public void setLastUpdated(Date lastUpdated) { + this.lastUpdated = lastUpdated; + } + + public void setPeerId(String peerId) { + this.peerId = peerId; + } + + public void setPeerName(String peerName) { + this.peerName = peerName; + } + + public void setPeerMsId(String peerMsId) { + this.peerMsId = peerMsId; + } + + public void setPeerRunId(String peerRunId) { + this.peerRunId = peerRunId; + } + + public void setPeerState(String peerState) { + this.peerState = peerState; + } + + public void setPeerServiceIp(String peerServiceIp) { + this.peerServiceIp = peerServiceIp; + } + + public void setPeerServicePort(String peerServicePort) { + this.peerServicePort = peerServicePort; + } +} 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 587fabfae8db..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,61 +30,69 @@ @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) + @Param(description = "comma-separated list of storage access groups for the pod", since = "4.21.0") + private String storageAccessGroups; + + @SerializedName(ApiConstants.ZONE_STORAGE_ACCESS_GROUPS) + @Param(description = "comma-separated list of storage access groups on the zone", since = "4.21.0") + private String zoneStorageAccessGroups; + public String getId() { return id; } @@ -184,4 +192,20 @@ public List getCapacities() { public void setCapacities(List capacities) { this.capacities = capacities; } + + public String getStorageAccessGroups() { + return storageAccessGroups; + } + + public void setStorageAccessGroups(String storageAccessGroups) { + this.storageAccessGroups = storageAccessGroups; + } + + public String getZoneStorageAccessGroups() { + return zoneStorageAccessGroups; + } + + public void setZoneStorageAccessGroups(String zoneStorageAccessGroups) { + this.zoneStorageAccessGroups = zoneStorageAccessGroups; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/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 65401eb2a029..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,29 +66,33 @@ 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") + private String domainPath; + @SerializedName(ApiConstants.STATE) @Param(description = "State of the gateway, can be Creating, Ready, Deleting") private String state; @@ -98,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) @@ -165,6 +169,10 @@ 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; 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 aab030741550..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,31 +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") + 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 @@ -110,6 +114,11 @@ public void setDomainName(String domainName) { this.domainName = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } + public void setUserId(String userId) { this.userId = userId; } public void setProjectRoleId(String projectRoleId) { 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 4462ea915680..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,39 +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") + 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) { @@ -87,6 +91,11 @@ public void setDomainName(String domain) { this.domainName = domain; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } + @Override public void setAccountName(String accountName) { this.accountName = accountName; 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 c43dd09b127a..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,179 +33,239 @@ 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") + @Param(description = "the total number of gpus the project can own", since = "4.21.0") + private String gpuLimit; + + @SerializedName("gputotal") + @Param(description = "the total number of gpus owned by project", since = "4.21.0") + private Long gpuTotal; + + @SerializedName("gpuavailable") + @Param(description = "the total number of gpus available to be created for this project", since = "4.21.0") + private String gpuAvailable; + @SerializedName("primarystoragelimit") - @Param(description = "the total primary storage space (in GiB) the project can own", since = "4.2.0") + @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) + @Param(description = "the total number of buckets which can be stored by this project", since = "4.21.0") + private String bucketLimit; + + @SerializedName(ApiConstants.BUCKET_TOTAL) + @Param(description = "the total number of buckets stored by this project", since = "4.21.0") + private Long bucketTotal; + + @SerializedName(ApiConstants.BUCKET_AVAILABLE) + @Param(description = "the total number of buckets available to this project", since = "4.21.0") + private String bucketAvailable; + + @SerializedName(ApiConstants.OBJECT_STORAGE_LIMIT) + @Param(description = "the total object storage space (in GiB) the project can own", since = "4.21.0") + private String objectStorageLimit; + + @SerializedName(ApiConstants.OBJECT_STORAGE_TOTAL) + @Param(description = "the total object storage space (in GiB) owned by the project", since = "4.21.0") + private Long objectStorageTotal; + + @SerializedName(ApiConstants.OBJECT_STORAGE_AVAILABLE) + @Param(description = "the total object storage space (in GiB) available to the project", since = "4.21.0") + 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) + @Param(description = "the total number of backups which can be stored by this project", since = "4.21.0") + private String backupLimit; + + @SerializedName(ApiConstants.BACKUP_TOTAL) + @Param(description = "the total number of backups stored by this project", since = "4.21.0") + private Long backupTotal; + + @SerializedName(ApiConstants.BACKUP_AVAILABLE) + @Param(description = "the total number of backups available to this project", since = "4.21.0") + private String backupAvailable; + + @SerializedName(ApiConstants.BACKUP_STORAGE_LIMIT) + @Param(description = "the total backup storage space (in GiB) the project can own", since = "4.21.0") + private String backupStorageLimit; + + @SerializedName(ApiConstants.BACKUP_STORAGE_TOTAL) + @Param(description = "the total backup storage space (in GiB) owned by the project", since = "4.21.0") + private Long backupStorageTotal; + + @SerializedName(ApiConstants.BACKUP_STORAGE_AVAILABLE) + @Param(description = "the total backup storage space (in GiB) available to the project", since = "4.21.0") + 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) @@ -213,9 +273,13 @@ 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) + @Param(description = "The tagged resource limit and count for the project", since = "4.20.0") + List taggedResources; + public void setId(String id) { this.id = id; } @@ -316,6 +380,36 @@ public void setSnapshotAvailable(String snapshotAvailable) { this.snapshotAvailable = snapshotAvailable; } + @Override + public void setBackupLimit(String backupLimit) { + this.backupLimit = backupLimit; + } + + @Override + public void setBackupTotal(Long backupTotal) { + this.backupTotal = backupTotal; + } + + @Override + public void setBackupAvailable(String backupAvailable) { + this.backupAvailable = backupAvailable; + } + + @Override + public void setBackupStorageLimit(String backupStorageLimit) { + this.backupStorageLimit = backupStorageLimit; + } + + @Override + public void setBackupStorageTotal(Long backupStorageTotal) { + this.backupStorageTotal = backupStorageTotal; + } + + @Override + public void setBackupStorageAvailable(String backupStorageAvailable) { + this.backupStorageAvailable = backupStorageAvailable; + } + @Override public void setTemplateLimit(String templateLimit) { this.templateLimit = templateLimit; @@ -401,6 +495,21 @@ public void setMemoryAvailable(String memoryAvailable) { this.memoryAvailable = memoryAvailable; } + @Override + public void setGpuLimit(String gpuLimit) { + this.gpuLimit = gpuLimit; + } + + @Override + public void setGpuTotal(Long gpuTotal) { + this.gpuTotal = gpuTotal; + } + + @Override + public void setGpuAvailable(String gpuAvailable) { + this.gpuAvailable = gpuAvailable; + } + @Override public void setPrimaryStorageLimit(String primaryStorageLimit) { this.primaryStorageLimit = primaryStorageLimit; @@ -431,6 +540,36 @@ public void setSecondaryStorageAvailable(String secondaryStorageAvailable) { this.secondaryStorageAvailable = secondaryStorageAvailable; } + @Override + public void setBucketLimit(String bucketLimit) { + this.bucketLimit = bucketLimit; + } + + @Override + public void setBucketTotal(Long bucketTotal) { + this.bucketTotal = bucketTotal; + } + + @Override + public void setBucketAvailable(String bucketAvailable) { + this.bucketAvailable = bucketAvailable; + } + + @Override + public void setObjectStorageLimit(String objectStorageLimit) { + this.objectStorageLimit = objectStorageLimit; + } + + @Override + public void setObjectStorageTotal(Long objectStorageTotal) { + this.objectStorageTotal = objectStorageTotal; + } + + @Override + public void setObjectStorageAvailable(String objectStorageAvailable) { + this.objectStorageAvailable = objectStorageAvailable; + } + public void setOwners(List> owners) { this.owners = owners; } @@ -447,4 +586,9 @@ public Date getCreated() { public void setCreated(Date created) { this.created = created; } + + @Override + public void setTaggedResourceLimitsAndCounts(List taggedResourceLimitsAndCounts) { + this.taggedResources = taggedResourceLimitsAndCounts; + } } 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/PurgeExpungedResourcesResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/PurgeExpungedResourcesResponse.java new file mode 100644 index 000000000000..3807d0d5b168 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/PurgeExpungedResourcesResponse.java @@ -0,0 +1,39 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package 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 PurgeExpungedResourcesResponse extends BaseResponse { + + @SerializedName(ApiConstants.RESOURCE_COUNT) + @Param(description = "The count of the purged expunged resources") + private Long resourceCount; + + public Long getResourceCount() { + return resourceCount; + } + + public void setResourceCount(Long resourceCount) { + this.resourceCount = resourceCount; + } +} 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/RegisterResponse.java deleted file mode 100644 index 5faedabfc16b..000000000000 --- a/api/src/main/java/org/apache/cloudstack/api/response/RegisterResponse.java +++ /dev/null @@ -1,49 +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.response; - -import com.google.gson.annotations.SerializedName; - -import org.apache.cloudstack.api.BaseResponse; - -import com.cloud.serializer.Param; - -public class RegisterResponse extends BaseResponse { - @SerializedName("apikey") - @Param(description = "the api key of the registered user", isSensitive = true) - private String apiKey; - - @SerializedName("secretkey") - @Param(description = "the secret key of the registered user", isSensitive = true) - private String secretKey; - - public String getApiKey() { - return apiKey; - } - - public void setApiKey(String apiKey) { - this.apiKey = apiKey; - } - - public String getSecretKey() { - return secretKey; - } - - public void setSecretKey(String secretKey) { - this.secretKey = secretKey; - } -} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/RegisterUserKeyResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/RegisterUserKeyResponse.java new file mode 100644 index 000000000000..4a5fb6b222e6 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/RegisterUserKeyResponse.java @@ -0,0 +1,58 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.response; + +import com.google.gson.annotations.SerializedName; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.serializer.Param; + +public class RegisterUserKeyResponse extends BaseResponse { + @SerializedName(ApiConstants.API_KEY) + @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) + private String secretKey; + + @SerializedName(ApiConstants.API_KEY_ACCESS) + @Param(description = "Whether API key access is allowed or not", isSensitive = true) + private Boolean apiKeyAccess; + + public String getApiKey() { + return apiKey; + } + + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + + public String getSecretKey() { + return secretKey; + } + + public void setSecretKey(String secretKey) { + this.secretKey = secretKey; + } + + public void setApiKeyAccess(Boolean apiKeyAccess) { + this.apiKeyAccess = apiKeyAccess; + } +} 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 0e078bea5bd7..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,51 +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") + 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) { @@ -104,6 +108,10 @@ public void setDomainName(String name) { this.domainName = name; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } public void setState(String state) { this.state = state; } 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 d0a4982f8724..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 @@ -16,48 +16,55 @@ // under the License. package org.apache.cloudstack.api.response; -import com.cloud.configuration.Resource; -import com.google.gson.annotations.SerializedName; - import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; +import com.cloud.configuration.Resource; import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; @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") + 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("resourcecount") - @Param(description = "resource count") + @SerializedName(ApiConstants.RESOURCE_COUNT) + @Param(description = "The resource count") private long resourceCount; + @SerializedName(ApiConstants.TAG) + @Param(description = "Tag for the resource", since = "4.20.0") + private String tag; + @Override public void setAccountName(String accountName) { this.accountName = accountName; @@ -73,6 +80,11 @@ public void setDomainName(String domainName) { this.domainName = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } + public void setResourceType(Resource.ResourceType resourceType) { this.resourceType = Integer.valueOf(resourceType.getOrdinal()).toString(); this.resourceTypeName = resourceType.getName(); @@ -92,4 +104,7 @@ public void setProjectName(String projectName) { this.projectName = projectName; } + public void setTag(String tag) { + this.tag = tag; + } } 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/ResourceLimitAndCountResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ResourceLimitAndCountResponse.java index f247be834cb1..66de71dd7634 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ResourceLimitAndCountResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ResourceLimitAndCountResponse.java @@ -20,6 +20,8 @@ package org.apache.cloudstack.api.response; +import java.util.List; + public interface ResourceLimitAndCountResponse { public void setNetworkLimit(String networkLimit); @@ -46,6 +48,12 @@ public interface ResourceLimitAndCountResponse { public void setMemoryAvailable(String memoryAvailable); + public void setGpuLimit(String gpuLimit); + + public void setGpuTotal(Long gpuTotal); + + public void setGpuAvailable(String gpuAvailable); + public void setPrimaryStorageLimit(String primaryStorageLimit); public void setPrimaryStorageTotal(Long primaryStorageTotal); @@ -82,6 +90,30 @@ public interface ResourceLimitAndCountResponse { public void setSnapshotAvailable(String snapshotAvailable); + public void setBackupLimit(String backupLimit); + + public void setBackupTotal(Long backupTotal); + + public void setBackupAvailable(String backupAvailable); + + public void setBackupStorageLimit(String backupStorageLimit); + + public void setBackupStorageTotal(Long backupStorageTotal); + + public void setBackupStorageAvailable(String backupStorageAvailable); + + void setBucketLimit(String bucketLimit); + + void setBucketTotal(Long bucketTotal); + + void setBucketAvailable(String bucketAvailable); + + void setObjectStorageLimit(String objectStorageLimit); + + void setObjectStorageTotal(Long objectStorageTotal); + + void setObjectStorageAvailable(String objectStorageAvailable); + public void setTemplateLimit(String templateLimit); public void setTemplateTotal(Long templateTotal); @@ -92,4 +124,6 @@ public interface ResourceLimitAndCountResponse { public void setVmRunning(Integer vmRunning); + public void setTaggedResourceLimitsAndCounts(List taggedResourceLimitsAndCounts); + } 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 13e1198177d7..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 @@ -16,51 +16,58 @@ // under the License. package org.apache.cloudstack.api.response; -import com.cloud.configuration.Resource; -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 com.cloud.configuration.Resource; import com.cloud.configuration.ResourceLimit; import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; @EntityReference(value = ResourceLimit.class) @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") + 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) + @Param(description = "The tag for the resource limit", since = "4.20.0") + private String tag; + @Override public void setAccountName(String accountName) { this.accountName = accountName; @@ -81,6 +88,10 @@ public void setDomainName(String domainName) { this.domainName = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } public void setResourceType(Resource.ResourceType resourceType) { this.resourceType = Integer.valueOf(resourceType.getOrdinal()).toString(); this.resourceTypeName = resourceType.getName(); @@ -94,4 +105,8 @@ public void setMax(Long max) { public void setProjectId(String projectId) { this.projectId = projectId; } + + public void setTag(String tag) { + this.tag = tag; + } } 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 443255605344..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,43 +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") + 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) { @@ -96,6 +100,11 @@ 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; 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 1861028f0ed5..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,13 +29,17 @@ 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) + @Param(description = "the state of the role") + private String state; + public void setRoleType(RoleType roleType) { if (roleType != null) { this.roleType = roleType.name(); @@ -45,4 +49,8 @@ public void setRoleType(RoleType roleType) { public void setIsDefault(Boolean isDefault) { this.isDefault = isDefault; } + + public void setState(String state) { + this.state = 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 f98cf0acd5dd..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.RESULT) - @Param(description = "result of the health check") + @SerializedName(ApiConstants.SUCCESS) + @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 c96421b0a400..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,55 +32,59 @@ 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) + @Param(description = "path of the Domain the security group belongs to", since = "4.19.2.0") + 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() { @@ -126,6 +130,11 @@ public void setDomainName(String domainName) { this.domainName = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } + public void setSecurityGroupIngressRules(Set securityGroupRules) { this.ingressRules = securityGroupRules; } 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 53767adf17d2..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,174 +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") + 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("storagetags") - @Param(description = "the tags for the service offering") + @SerializedName(ApiConstants.STORAGE_TAGS) + @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) @@ -205,31 +209,83 @@ 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) + @Param(description = "the ID of the gpu card to which service offering is linked", since = "4.21") + private String gpuCardId; + + @SerializedName(ApiConstants.GPU_CARD_NAME) + @Param(description = "the name of the gpu card to which service offering is linked", since = "4.21") + private String gpuCardName; + + @SerializedName(ApiConstants.VGPU_PROFILE_ID) + @Param(description = "the ID of the vgpu profile to which service offering is linked", since = "4.21") + private String vgpuProfileId; + + @SerializedName(ApiConstants.VGPU_PROFILE_NAME) + @Param(description = "the name of the vgpu profile to which service offering is linked", since = "4.21") + private String vgpuProfileName; + + @SerializedName(ApiConstants.VIDEORAM) + @Param(description = "the video RAM size in MB") + private Long videoRam; + + @SerializedName(ApiConstants.MAXHEADS) + @Param(description = "the maximum number of display heads") + private Long maxHeads; + + @SerializedName(ApiConstants.MAXRESOLUTIONX) + @Param(description = "the maximum X resolution") + private Long maxResolutionX; + + @SerializedName(ApiConstants.MAXRESOLUTIONY) + @Param(description = "the maximum Y resolution") + private Long maxResolutionY; + + @SerializedName(ApiConstants.GPU_COUNT) + @Param(description = "the count of GPUs to attach ", since = "4.21") + private Integer gpuCount; + + @SerializedName(ApiConstants.GPU_DISPLAY) + @Param(description = "whether GPU device is used for display or not ", since = "4.21") + private Boolean gpuDisplay; + + @SerializedName(ApiConstants.PURGE_RESOURCES) + @Param(description = "Whether to cleanup VM and its associated resource upon expunge", since = "4.20") + private Boolean purgeResources; + + @SerializedName(ApiConstants.INSTANCE_LEASE_DURATION) + @Param(description = "Instance lease duration (in days) for service offering", since = "4.21.0") + private Integer leaseDuration; + + @SerializedName(ApiConstants.INSTANCE_LEASE_EXPIRY_ACTION) + @Param(description = "Action to be taken once lease is over", since = "4.21.0") + private String leaseExpiryAction; + public ServiceOfferingResponse() { } @@ -249,6 +305,14 @@ public void setName(String name) { this.name = name; } + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + public Boolean getIsSystem() { return isSystem; } @@ -489,6 +553,22 @@ public void setCacheMode(String cacheMode) { this.cacheMode = cacheMode; } + public Integer getLeaseDuration() { + return leaseDuration; + } + + public void setLeaseDuration(Integer leaseDuration) { + this.leaseDuration = leaseDuration; + } + + public String getLeaseExpiryAction() { + return leaseExpiryAction; + } + + public void setLeaseExpiryAction(String leaseExpiryAction) { + this.leaseExpiryAction = leaseExpiryAction; + } + public String getVsphereStoragePolicy() { return vsphereStoragePolicy; } @@ -543,4 +623,88 @@ public String getDiskOfferingDisplayText() { } public void setEncryptRoot(Boolean encrypt) { this.encryptRoot = encrypt; } + + public String getVgpuProfileName() { + return vgpuProfileName; + } + + public void setVgpuProfileName(String vgpuProfileName) { + this.vgpuProfileName = vgpuProfileName; + } + + public Long getVideoRam() { + return videoRam; + } + + public void setVideoRam(Long videoRam) { + this.videoRam = videoRam; + } + + public Long getMaxHeads() { + return maxHeads; + } + + public void setMaxHeads(Long maxHeads) { + this.maxHeads = maxHeads; + } + + public Long getMaxResolutionX() { + return maxResolutionX; + } + + public void setMaxResolutionX(Long maxResolutionX) { + this.maxResolutionX = maxResolutionX; + } + + public Long getMaxResolutionY() { + return maxResolutionY; + } + + public void setMaxResolutionY(Long maxResolutionY) { + this.maxResolutionY = maxResolutionY; + } + + public String getVgpuProfileId() { + return vgpuProfileId; + } + + public void setVgpuProfileId(String vgpuProfileId) { + this.vgpuProfileId = vgpuProfileId; + } + + public String getGpuCardName() { + return gpuCardName; + } + + public void setGpuCardName(String gpuCardName) { + this.gpuCardName = gpuCardName; + } + + public String getGpuCardId() { + return gpuCardId; + } + + public void setGpuCardId(String gpuCardId) { + this.gpuCardId = gpuCardId; + } + + public Integer getGpuCount() { + return gpuCount; + } + + public void setGpuCount(Integer gpuCount) { + this.gpuCount = gpuCount; + } + + public Boolean getGpuDisplay() { + return gpuDisplay; + } + + public void setGpuDisplay(Boolean gpuDisplay) { + this.gpuDisplay = gpuDisplay; + } + + public void setPurgeResources(Boolean purgeResources) { + this.purgeResources = purgeResources; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/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/SharedFSProviderResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/SharedFSProviderResponse.java new file mode 100644 index 000000000000..4d92945646f6 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/SharedFSProviderResponse.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.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 SharedFSProviderResponse extends BaseResponse { + @SerializedName(ApiConstants.NAME) + @Param(description = "the name of the shared filesystem provider") + private String name; + + 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/SharedFSResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/SharedFSResponse.java new file mode 100644 index 000000000000..bac348fe36e4 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/SharedFSResponse.java @@ -0,0 +1,369 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.BaseResponseWithTagInformation; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.storage.sharedfs.SharedFS; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +import java.util.ArrayList; +import java.util.List; + + +@EntityReference(value = SharedFS.class) +public class SharedFSResponse extends BaseResponseWithTagInformation implements ControlledViewEntityResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the shared filesystem") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "name of the shared filesystem") + private String name; + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "description of the shared filesystem") + private String description; + + @SerializedName(ApiConstants.ZONE_ID) + @Param(description = "ID of the availability zone") + private String zoneId; + + @SerializedName(ApiConstants.ZONE_NAME) + @Param(description = "Name of the availability zone") + private String zoneName; + + @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) + @Param(description = "ID of the storage fs vm") + private String virtualMachineId; + + @SerializedName(ApiConstants.VIRTUAL_MACHINE_STATE) + @Param(description = "ID of the storage fs vm") + private String virtualMachineState; + + @SerializedName(ApiConstants.VOLUME_NAME) + @Param(description = "name of the storage fs data volume") + private String volumeName; + + @SerializedName(ApiConstants.VOLUME_ID) + @Param(description = "ID of the storage fs data volume") + private String volumeId; + + @SerializedName(ApiConstants.STORAGE) + @Param(description = "name of the storage pool hosting the data volume") + private String storagePoolName; + + @SerializedName(ApiConstants.STORAGE_ID) + @Param(description = "ID of the storage pool hosting the data volume") + private String storagePoolId; + + @SerializedName(ApiConstants.SIZE) + @Param(description = "size of the shared filesystem") + private Long size; + + @SerializedName(ApiConstants.SIZEGB) + @Param(description = "size of the shared filesystem in GiB") + private String sizeGB; + + @SerializedName(ApiConstants.DISK_OFFERING_ID) + @Param(description = "disk offering ID for the shared filesystem") + private String diskOfferingId; + + @SerializedName("diskofferingname") + @Param(description = "disk offering for the shared filesystem") + private String diskOfferingName; + + @SerializedName("iscustomdiskoffering") + @Param(description = "disk offering for the shared filesystem has custom size") + private Boolean isCustomDiskOffering; + + @SerializedName("diskofferingdisplaytext") + @Param(description = "disk offering display text for the shared filesystem") + private String diskOfferingDisplayText; + + @SerializedName(ApiConstants.SERVICE_OFFERING_ID) + @Param(description = "service offering ID for the shared filesystem") + private String serviceOfferingId; + + @SerializedName("serviceofferingname") + @Param(description = "service offering for the shared filesystem") + private String serviceOfferingName; + + @SerializedName(ApiConstants.NETWORK_ID) + @Param(description = "Network ID of the shared filesystem") + private String networkId; + + @SerializedName(ApiConstants.NETWORK_NAME) + @Param(description = "Network name of the shared filesystem") + private String networkName; + + @SerializedName(ApiConstants.NIC) + @Param(description = "the list of nics associated with the shared filesystem", responseObject = NicResponse.class) + private List nics; + + @SerializedName(ApiConstants.PATH) + @Param(description = "path to mount the shared filesystem") + private String path; + + @SerializedName(ApiConstants.STATE) + @Param(description = "the state of the shared filesystem") + private String state; + + @SerializedName(ApiConstants.PROVIDER) + @Param(description = "the shared filesystem provider") + private String provider; + + @SerializedName(ApiConstants.FILESYSTEM) + @Param(description = "the filesystem format") + private String filesystem; + + @SerializedName(ApiConstants.ACCOUNT) + @Param(description = "the account associated with the shared filesystem") + private String accountName; + + @SerializedName(ApiConstants.PROJECT_ID) + @Param(description = "the project ID of the shared filesystem") + private String projectId; + + @SerializedName(ApiConstants.PROJECT) + @Param(description = "the project name of the shared filesystem") + private String projectName; + + @SerializedName(ApiConstants.DOMAIN_ID) + @Param(description = "the ID of the domain associated with the shared filesystem") + private String domainId; + + @SerializedName(ApiConstants.DOMAIN) + @Param(description = "the domain associated with the shared filesystem") + private String domainName; + + @SerializedName(ApiConstants.DOMAIN_PATH) + @Param(description = "path of the domain to which the shared filesystem") + private String domainPath; + + @SerializedName(ApiConstants.PROVISIONINGTYPE) + @Param(description = "provisioning type used in the shared filesystem") + private String provisioningType; + + @SerializedName(ApiConstants.DISK_IO_READ) + @Param(description = "the read (IO) of disk on the shared filesystem") + private Long diskIORead; + + @SerializedName(ApiConstants.DISK_IO_WRITE) + @Param(description = "the write (IO) of disk on the shared filesystem") + private Long diskIOWrite; + + @SerializedName(ApiConstants.DISK_KBS_READ) + @Param(description = "the shared filesystem's disk read in KiB") + private Long diskKbsRead; + + @SerializedName(ApiConstants.DISK_KBS_WRITE) + @Param(description = "the shared filesystem's disk write in KiB") + private Long diskKbsWrite; + + @SerializedName(ApiConstants.VIRTUAL_SIZE) + @Param(description = "the bytes allocated") + private Long virtualSize; + + @SerializedName(ApiConstants.PHYSICAL_SIZE) + @Param(description = "the bytes actually consumed on disk") + private Long physicalSize; + + @SerializedName(ApiConstants.UTILIZATION) + @Param(description = "the disk utilization") + private String utilization; + + @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; + } + + @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; + } + + public void setId(String id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + public void setZoneId(String zoneId) { + this.zoneId = zoneId; + } + + public void setZoneName(String zoneName) { + this.zoneName = zoneName; + } + + public void setVirtualMachineId(String virtualMachineId) { + this.virtualMachineId = virtualMachineId; + } + + public void setState(String state) { + this.state = state; + } + + public void setVolumeId(String volumeId) { + this.volumeId = volumeId; + } + + public void setNetworkId(String networkId) { + this.networkId = networkId; + } + + public void setNetworkName(String networkName) { + this.networkName = networkName; + } + + public List getNics() { + return nics; + } + + public void addNic(NicResponse nic) { + if (this.nics == null) { + this.nics = new ArrayList<>(); + } + this.nics.add(nic); + } + + public void setSize(Long size) { + this.size = size; + } + + public void setDescription(String description) { + this.description = description; + } + + public void setPath(String path) { + this.path = path; + } + + public void setVolumeName(String volumeName) { + this.volumeName = volumeName; + } + + public void setStoragePoolName(String storagePoolName) { + this.storagePoolName = storagePoolName; + } + + public void setStoragePoolId(String storagePoolId) { + this.storagePoolId = storagePoolId; + } + + public void setProvider(String provider) { + this.provider = provider; + } + + public void setFilesystem(String filesystem) { + this.filesystem = filesystem; + } + + public void setSizeGB(Long size) { + if (size != null) { + this.sizeGB = String.format("%.2f GiB", size / (1024.0 * 1024.0 * 1024.0)); + } + } + + public void setDiskOfferingId(String diskOfferingId) { + this.diskOfferingId = diskOfferingId; + } + + public void setDiskOfferingName(String diskOfferingName) { + this.diskOfferingName = diskOfferingName; + } + + public void setDiskOfferingDisplayText(String diskOfferingDisplayText) { + this.diskOfferingDisplayText = diskOfferingDisplayText; + } + + public void setServiceOfferingId(String serviceOfferingId) { + this.serviceOfferingId = serviceOfferingId; + } + + public void setServiceOfferingName(String serviceOfferingName) { + this.serviceOfferingName = serviceOfferingName; + } + + public void setProvisioningType(String provisioningType) { + this.provisioningType = provisioningType; + } + + public void setDiskIORead(Long diskIORead) { + this.diskIORead = diskIORead; + } + + public void setDiskIOWrite(Long diskIOWrite) { + this.diskIOWrite = diskIOWrite; + } + + public void setDiskKbsRead(Long diskKbsRead) { + this.diskKbsRead = diskKbsRead; + } + + public void setDiskKbsWrite(Long diskKbsWrite) { + this.diskKbsWrite = diskKbsWrite; + } + + public void setVirtualSize(Long virtualSize) { + this.virtualSize = virtualSize; + } + + public void setPhysicalSize(Long physicalSize) { + this.physicalSize = physicalSize; + } + + public void setUtilization(String utilization) { + this.utilization = utilization; + } + + public void setIsCustomDiskOffering(Boolean isCustomDiskOffering) { + this.isCustomDiskOffering = isCustomDiskOffering; + } + + public void setVirtualMachineState(String virtualMachineState) { + this.virtualMachineState = virtualMachineState; + } +} 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 babc9bf4432d..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,45 +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") + 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; } @@ -193,4 +205,17 @@ public void setDomainName(String domainName) { this.domain = domainName; } + @Override + 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 1f7509239d1a..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,61 +83,65 @@ 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) + @Param(description = "the domain path of the owner", since = "4.19.2.0") + 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) { @@ -241,6 +245,11 @@ public void setDomainName(String domainName) { this.domain = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } + public void setForDisplay(Boolean forDisplay) { this.forDisplay = forDisplay; } 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 cdd8e4f3d87f..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,47 +31,51 @@ @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) + @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.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) { @@ -119,6 +123,10 @@ public void setDomainName(String domainName) { this.domain = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } public void setForDisplay(Boolean forDisplay) { this.forDisplay = forDisplay; } 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 bfa1cca1ca08..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 @@ -16,55 +16,63 @@ // under the License. package org.apache.cloudstack.api.response; -import java.util.LinkedHashSet; -import java.util.Set; - +import com.cloud.serializer.Param; +import com.cloud.storage.snapshot.SnapshotPolicy; +import com.google.gson.annotations.SerializedName; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponseWithTagInformation; import org.apache.cloudstack.api.EntityReference; -import com.cloud.serializer.Param; -import com.cloud.storage.snapshot.SnapshotPolicy; -import com.google.gson.annotations.SerializedName; +import java.util.LinkedHashSet; +import java.util.Set; @EntityReference(value = SnapshotPolicy.class) public class SnapshotPolicyResponse extends BaseResponseWithTagInformation { @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) @Param(description = "The list of zones in which snapshot backup is scheduled", responseObject = ZoneResponse.class, since = "4.19.0") protected Set zones; + @SerializedName(ApiConstants.STORAGE) + @Param(description = "The list of pools in which snapshot backup is scheduled", responseObject = StoragePoolResponse.class, since = "4.21.0") + protected Set storagePools; + public SnapshotPolicyResponse() { tags = new LinkedHashSet(); zones = new LinkedHashSet<>(); + storagePools = new LinkedHashSet<>(); } public String getId() { @@ -83,6 +91,10 @@ public void setVolumeId(String volumeId) { this.volumeId = volumeId; } + public void setVolumeName(String volumeName) { + this.volumeName = volumeName; + } + public String getSchedule() { return schedule; } @@ -130,4 +142,6 @@ public void setTags(Set tags) { public void setZones(Set zones) { this.zones = zones; } + + public void setStoragePools(Set pools) { this.storagePools = pools; } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/SnapshotResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/SnapshotResponse.java index e160f64ebe91..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,31 +32,35 @@ @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) + @Param(description = "path of the Domain the snapshot's account belongs to", since = "4.19.2.0") + 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) @@ -64,31 +68,35 @@ 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) + @Param(description = "state of the disk volume") + 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) @@ -96,11 +104,15 @@ 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) + @Param(description = "chain size of snapshot including all parent snapshots. Shown only for incremental snapshots if snapshot.show.chain.size setting is set to true", since = "4.21.0") + private Long chainSize; + @SerializedName(ApiConstants.ZONE_ID) - @Param(description = "id of the availability zone") + @Param(description = "ID of the availability zone") private String zoneId; @SerializedName(ApiConstants.ZONE_NAME) @@ -108,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) @@ -143,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(); } @@ -183,6 +203,11 @@ public void setDomainName(String domainName) { this.domainName = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } + public void setSnapshotType(String snapshotType) { this.snapshotType = snapshotType; } @@ -199,6 +224,10 @@ public void setVolumeType(String volumeType) { this.volumeType = volumeType; } + public void setVolumeState(String volumeState) { + this.volumeState = volumeState; + } + public void setCreated(Date created) { this.created = created; } @@ -227,6 +256,10 @@ public void setPhysicalSize(long physicalSize) { this.physicalSize = physicalSize; } + public void setChainSize(long chainSize) { + this.chainSize = chainSize; + } + @Override public void setProjectId(String projectId) { this.projectId = projectId; @@ -288,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 79c7f9040a17..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,47 +31,59 @@ @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) @Param(description = "VPC the static route belongs to") private String vpcId; - @SerializedName(ApiConstants.GATEWAY_ID) + @SerializedName(ApiConstants.VPC_GATEWAY_ID) @Param(description = "VPC gateway the route is created for") - private String gatewayId; + private String vpcGatewayId; + + @SerializedName(ApiConstants.VPC_GATEWAY_IP) + @Param(description = "IP of VPC gateway the route is created for", since = "4.21.0") + private String vpcGatewayIp; + + @SerializedName(ApiConstants.NEXT_HOP) + @Param(description = "Next hop of the static route", since = "4.21.0") + private String nextHop; @SerializedName(ApiConstants.CIDR) - @Param(description = "static route CIDR") + @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) + @Param(description = "the domain path associated with the static route", since = "4.19.2.0") + 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 @@ -91,8 +103,16 @@ public void setVpcId(String vpcId) { this.vpcId = vpcId; } - public void setGatewayId(String gatewayId) { - this.gatewayId = gatewayId; + public void setVpcGatewayId(String vpcGatewayId) { + this.vpcGatewayId = vpcGatewayId; + } + + public void setVpcGatewayIp(String vpcGatewayIp) { + this.vpcGatewayIp = vpcGatewayIp; + } + + public void setNextHop(String nextHop) { + this.nextHop = nextHop; } public void setCidr(String cidr) { @@ -114,6 +134,10 @@ 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; 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 5dd76fa5eef6..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/StorageAccessGroupResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/StorageAccessGroupResponse.java new file mode 100644 index 000000000000..a6324dd62a9f --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/StorageAccessGroupResponse.java @@ -0,0 +1,108 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.response; + +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import com.cloud.serializer.Param; + +public class StorageAccessGroupResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) + @Param(description = "the ID of the storage access group") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "the name of the storage access group") + private String name; + + @SerializedName("hosts") + @Param(description = "List of Hosts in the Storage Access Group") + private ListResponse hostResponseList; + + @SerializedName("clusters") + @Param(description = "List of Clusters in the Storage Access Group") + private ListResponse clusterResponseList; + + @SerializedName("pods") + @Param(description = "List of Pods in the Storage Access Group") + private ListResponse podResponseList; + + @SerializedName("zones") + @Param(description = "List of Zones in the Storage Access Group") + private ListResponse zoneResponseList; + + @SerializedName("storagepools") + @Param(description = "List of Storage Pools in the Storage Access Group") + private ListResponse storagePoolResponseList; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public ListResponse getHostResponseList() { + return hostResponseList; + } + + public void setHostResponseList(ListResponse hostResponseList) { + this.hostResponseList = hostResponseList; + } + + public ListResponse getClusterResponseList() { + return clusterResponseList; + } + + public void setClusterResponseList(ListResponse clusterResponseList) { + this.clusterResponseList = clusterResponseList; + } + + public ListResponse getPodResponseList() { + return podResponseList; + } + + public void setPodResponseList(ListResponse podResponseList) { + this.podResponseList = podResponseList; + } + + public ListResponse getZoneResponseList() { + return zoneResponseList; + } + + public void setZoneResponseList(ListResponse zoneResponseList) { + this.zoneResponseList = zoneResponseList; + } + + public ListResponse getStoragePoolResponseList() { + return storagePoolResponseList; + } + + public void setStoragePoolResponseList(ListResponse storagePoolResponseList) { + this.storagePoolResponseList = storagePoolResponseList; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/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 183290ec9ebe..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,99 +30,120 @@ @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) + @Param(description = "total IOPS currently in use", since = "4.20.1") + private Long usedIops; + + @SerializedName(ApiConstants.STORAGE_CUSTOM_STATS) + @Param(description = "the storage pool custom stats", since = "4.18.1") + 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) + @Param(description = "the storage access groups for the storage pool", since = "4.21.0") + private String storageAccessGroups; + + @SerializedName(ApiConstants.NFS_MOUNT_OPTIONS) + @Param(description = "the nfs mount options for the storage pool", since = "4.19.1") + private String nfsMountOpts; + @SerializedName(ApiConstants.IS_TAG_A_RULE) @Param(description = ApiConstants.PARAMETER_DESCRIPTION_IS_TAG_A_RULE) 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") @@ -130,9 +151,17 @@ 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) + @Param(description = "whether this pool is managed or not") + private Boolean managed; + + @SerializedName(ApiConstants.DETAILS) + @Param(description = "the storage pool details") + private Map details; + public Map getCaps() { return caps; } @@ -264,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; } @@ -300,6 +337,22 @@ public void setAllocatedIops(Long allocatedIops) { this.allocatedIops = allocatedIops; } + public Long getUsedIops() { + return usedIops; + } + + public void setUsedIops(Long usedIops) { + this.usedIops = usedIops; + } + + public Map getCustomStats() { + return customStats; + } + + public void setCustomStats(Map customStats) { + this.customStats = customStats; + } + public String getTags() { return tags; } @@ -308,6 +361,14 @@ public void setTags(String tags) { this.tags = tags; } + public String getStorageAccessGroups() { + return storageAccessGroups; + } + + public void setStorageAccessGroups(String storageAccessGroups) { + this.storageAccessGroups = storageAccessGroups; + } + public Boolean getIsTagARule() { return isTagARule; } @@ -347,4 +408,40 @@ public String getProvider() { public void setProvider(String provider) { this.provider = provider; } + + public String getNfsMountOpts() { + return nfsMountOpts; + } + + public void setNfsMountOpts(String nfsMountOpts) { + this.nfsMountOpts = nfsMountOpts; + } + + public Long getAllocatedIops() { + return allocatedIops; + } + + public Boolean getTagARule() { + return isTagARule; + } + + public void setTagARule(Boolean tagARule) { + isTagARule = tagARule; + } + + public Boolean getManaged() { + return managed; + } + + public void setManaged(Boolean managed) { + this.managed = managed; + } + + public Map getDetails() { + return details; + } + + public void setDetails(Map details) { + this.details = details; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/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 31a8b731491c..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,151 +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("jobid") - @Param(description = "the job ID associated with the system VM. This is only displayed if the router listed is part of a currently running asynchronous job.") - private String jobId; - - @SerializedName("jobstatus") - @Param(description = "the job status associated with the system VM. This is only displayed if the router listed is part of a currently running asynchronous job.") - private Integer jobStatus; - @SerializedName("zoneid") - @Param(description = "the Zone ID for the system VM") + @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) @@ -186,6 +182,10 @@ public class SystemVmResponse extends BaseResponseWithAnnotations { @Param(description = "the name of the service offering of the system virtual machine.") private String serviceOfferingName; + @SerializedName(ApiConstants.ARCH) + @Param(description = "CPU arch of the system VM", since = "4.20.1") + private String arch; + @Override public String getObjectId() { return this.getId(); @@ -359,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; } @@ -490,4 +498,8 @@ public String getServiceOfferingName() { public void setServiceOfferingName(String serviceOfferingName) { this.serviceOfferingName = serviceOfferingName; } + + public void setArch(String arch) { + this.arch = arch; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/TaggedResourceLimitAndCountResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/TaggedResourceLimitAndCountResponse.java new file mode 100644 index 000000000000..bfb03b7667c2 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/TaggedResourceLimitAndCountResponse.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.response; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.configuration.Resource; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class TaggedResourceLimitAndCountResponse extends BaseResponse { + + @SerializedName(ApiConstants.RESOURCE_TYPE) + @Param(description = "Numerical value for the type of the resource. See the ResourceType for more information on these values.") + private Integer resourceType; + + @SerializedName(ApiConstants.RESOURCE_TYPE_NAME) + @Param(description = "Name for the type of the resource") + private String resourceTypeName; + + @SerializedName(ApiConstants.TAG) + @Param(description = "The tag for the resource type") + private String tag; + + @SerializedName(ApiConstants.LIMIT) + @Param(description = "The limit for the resource count for the type and tag for the owner") + private Long limit; + + @SerializedName(ApiConstants.TOTAL) + @Param(description = "The total amount of the resource for the type and tag that is used by the owner") + private Long total; + + @SerializedName(ApiConstants.AVAILABLE) + @Param(description = "The available amount of the resource for the type and tag that is available for the owner") + private Long available; + + + public void setResourceType(Resource.ResourceType resourceType) { + this.resourceType = resourceType.getOrdinal(); + this.resourceTypeName = resourceType.getName(); + } + + public void setTag(String tag) { + this.tag = tag; + } + + public void setLimit(Long limit) { + this.limit = limit; + } + + public void setTotal(Long total) { + this.total = total; + } + + public void setAvailable(Long available) { + this.available = available; + } + + public Long getLimit() { + return limit; + } + + public Long getTotal() { + return total; + } + + public Long getAvailable() { + return available; + } +} 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 3abd44941d94..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,209 +36,230 @@ @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") + 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) + @Param(description = "CPU Arch of the template", since = "4.20") + 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; + @SerializedName(ApiConstants.FOR_CKS) + @Param(description = "If true it indicates that the template can be used for CKS cluster deployments", + since = "4.21.0") + private Boolean forCks; + @SerializedName(ApiConstants.DEPLOY_AS_IS_DETAILS) - @Param(description = "VMware only: additional key/value details tied with deploy-as-is template", + @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") + private String extensionId; + + @SerializedName(ApiConstants.EXTENSION_NAME) @Param(description="The name of extension linked to this template", since = "4.21.0") + private String extensionName; + public TemplateResponse() { tags = new LinkedHashSet<>(); } @@ -277,6 +298,14 @@ public void setOsTypeName(String osTypeName) { this.osTypeName = osTypeName; } + public Long getOsTypeCategoryId() { + return osTypeCategoryId; + } + + public void setOsTypeCategoryId(Long osTypeCategoryId) { + this.osTypeCategoryId = osTypeCategoryId; + } + public void setId(String id) { this.id = id; } @@ -354,6 +383,11 @@ public void setDomainName(String domainName) { this.domainName = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } + @Override public void setDomainId(String domainId) { this.domainId = domainId; @@ -440,6 +474,10 @@ public void setDeployAsIs(Boolean deployAsIs) { this.deployAsIs = deployAsIs; } + public void setForCks(Boolean forCks) { + this.forCks = forCks; + } + public void setParentTemplateId(String parentTemplateId) { this.parentTemplateId = parentTemplateId; } @@ -511,4 +549,24 @@ public String getUserDataParams() { public void setUserDataParams(String userDataParams) { this.userDataParams = userDataParams; } + + public void setArch(String arch) { + this.arch = arch; + } + + public String getExtensionId() { + return extensionId; + } + + public void setExtensionId(String extensionId) { + this.extensionId = extensionId; + } + + public String getExtensionName() { + return extensionName; + } + + public void setExtensionName(String extensionName) { + this.extensionName = extensionName; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/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 cec70f20cff7..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 @@ -24,14 +24,18 @@ public class UnmanageVMInstanceResponse extends BaseResponse { - @SerializedName(ApiConstants.RESULT) - @Param(description = "result of the unmanage VM operation") + @SerializedName(ApiConstants.SUCCESS) + @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 7a26b1785919..195323b741d2 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,47 +44,55 @@ 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.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) + @Param(description = "indicates the boot mode") + private String bootMode; + + @SerializedName(ApiConstants.BOOT_TYPE) + @Param(description = "indicates the boot type") + private String bootType; + @SerializedName(ApiConstants.DISK) - @Param(description = "the list of disks associated with the virtual machine", responseObject = UnmanagedInstanceDiskResponse.class) + @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() { @@ -211,4 +219,20 @@ public void setNics(Set nics) { public void addNic(NicResponse nic) { this.nics.add(nic); } + + public String getBootMode() { + return bootMode; + } + + public void setBootMode(String bootMode) { + this.bootMode = bootMode; + } + + public String getBootType() { + return bootType; + } + + public void setBootType(String bootType) { + this.bootType = bootType; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/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 7bcb1afd2d24..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 @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.response; +import java.util.Date; import java.util.LinkedHashSet; import java.util.Set; @@ -28,127 +29,131 @@ @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") + 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") - private String startDate; + @Param(description = "Start date of the usage record") + private Date startDate; @SerializedName(ApiConstants.END_DATE) - @Param(description = "end date of the usage record") - private String endDate; + @Param(description = "End date of the usage record") + private Date endDate; @SerializedName("issourcenat") @Param(description = "True if the IPAddress is source NAT") 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") @@ -156,11 +161,11 @@ 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() { - tags = new LinkedHashSet(); + tags = new LinkedHashSet<>(); } public void setTags(Set tags) { @@ -245,11 +250,11 @@ public void setSize(Long size) { this.size = size; } - public void setStartDate(String startDate) { + public void setStartDate(Date startDate) { this.startDate = startDate; } - public void setEndDate(String endDate) { + public void setEndDate(Date endDate) { this.endDate = endDate; } @@ -276,6 +281,10 @@ public void setDomainName(String domainName) { this.domainName = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } public void setNetworkId(String networkId) { this.networkId = networkId; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/UsageTypeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/UsageTypeResponse.java index 83b97f00c151..5beef5ac5562 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/UsageTypeResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/UsageTypeResponse.java @@ -25,12 +25,16 @@ public class UsageTypeResponse extends BaseResponse { - @SerializedName("usagetypeid") - @Param(description = "usage type") + @SerializedName("id") + @Param(description = "Usage type ID") private Integer usageType; + @SerializedName(ApiConstants.NAME) + @Param(description = "Usage type name") + private String name; + @SerializedName(ApiConstants.DESCRIPTION) - @Param(description = "description of usage type") + @Param(description = "Usage type description") private String description; public String getDescription() { @@ -49,10 +53,10 @@ public void setUsageType(Integer usageType) { this.usageType = usageType; } - public UsageTypeResponse(Integer usageType, String description) { + public UsageTypeResponse(Integer usageType, String name, String description) { this.usageType = usageType; + this.name = name; this.description = description; setObjectName("usagetype"); } - } 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 bbe27f845207..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 @@ -24,32 +24,44 @@ import org.apache.cloudstack.api.EntityReference; @EntityReference(value = UserData.class) -public class UserDataResponse extends BaseResponseWithAnnotations { +public class UserDataResponse extends BaseResponseWithAnnotations implements ControlledEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "ID of the ssh keypair") + @Param(description = "ID of the User Data") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "Name of the userdata") + @Param(description = "Name of the User Data") private String name; - @SerializedName(ApiConstants.ACCOUNT_ID) @Param(description="the owner id of the userdata") + @SerializedName(ApiConstants.ACCOUNT_ID) @Param(description="The owner id of the User Data") private String accountId; - @SerializedName(ApiConstants.ACCOUNT) @Param(description="the owner of the userdata") + @SerializedName(ApiConstants.ACCOUNT) @Param(description="The owner of the User Data") private String accountName; - @SerializedName(ApiConstants.DOMAIN_ID) @Param(description="the domain id of the userdata owner") + @SerializedName(ApiConstants.PROJECT_ID) + @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") + private String projectName; + + @SerializedName(ApiConstants.DOMAIN_ID) @Param(description="The domain id of the User Data owner") private String domainId; - @SerializedName(ApiConstants.DOMAIN) @Param(description="the domain name of the userdata owner") + @SerializedName(ApiConstants.DOMAIN) @Param(description="The domain name of the User Data owner") private String domain; - @SerializedName(ApiConstants.USER_DATA) @Param(description="base64 encoded userdata content") + @SerializedName(ApiConstants.DOMAIN_PATH) + @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") private String userData; - @SerializedName(ApiConstants.PARAMS) @Param(description="list of parameters which contains the list of keys or string parameters that are needed to be passed for any variables declared in userdata") + @SerializedName(ApiConstants.PARAMS) @Param(description="List of parameters which contains the list of keys or string parameters that are needed to be passed for any variables declared in the User Data") private String params; public UserDataResponse() { @@ -118,6 +130,16 @@ 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 getDomainName() { return domain; } @@ -125,4 +147,9 @@ public String getDomainName() { public void setDomainName(String domain) { this.domain = domain; } + + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } } 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 1a17f3b86988..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("usersource") - @Param(description = "the source type of the user in lowercase, such as native, ldap, saml2") + @SerializedName(ApiConstants.USER_SOURCE) + @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,13 +121,17 @@ 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") + ApiConstants.ApiKeyAccess apiKeyAccess; + @Override public String getObjectId() { return this.getId(); @@ -309,4 +313,8 @@ public Boolean getIs2FAmandated() { public void set2FAmandated(Boolean is2FAmandated) { this.is2FAmandated = is2FAmandated; } + + public void setApiKeyAccess(Boolean apiKeyAccess) { + this.apiKeyAccess = ApiConstants.ApiKeyAccess.fromBoolean(apiKeyAccess); + } } 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 763265e109d3..a7f6dff96f88 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java @@ -31,6 +31,7 @@ import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponseWithTagInformation; import org.apache.cloudstack.api.EntityReference; +import org.apache.commons.collections.CollectionUtils; import com.cloud.network.router.VirtualRouter; import com.cloud.serializer.Param; @@ -40,303 +41,359 @@ @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") + 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") + 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(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) + @Param(description = "the ID of the gpu card to which service offering is linked", since = "4.21") + private String gpuCardId; + + @SerializedName(ApiConstants.GPU_CARD_NAME) + @Param(description = "the name of the gpu card to which service offering is linked", since = "4.21") + private String gpuCardName; + + @SerializedName(ApiConstants.VGPU_PROFILE_ID) + @Param(description = "the ID of the vgpu profile to which service offering is linked", since = "4.21") + private String vgpuProfileId; + + @SerializedName(ApiConstants.VGPU_PROFILE_NAME) + @Param(description = "the name of the vgpu profile to which service offering is linked", since = "4.21") + private String vgpuProfileName; + + @SerializedName(ApiConstants.VIDEORAM) + @Param(description = "the video RAM size in MB") + private Long videoRam; + + @SerializedName(ApiConstants.MAXHEADS) + @Param(description = "the maximum number of display heads") + private Long maxHeads; + + @SerializedName(ApiConstants.MAXRESOLUTIONX) + @Param(description = "the maximum X resolution") + private Long maxResolutionX; + + @SerializedName(ApiConstants.MAXRESOLUTIONY) + @Param(description = "the maximum Y resolution") + private Long maxResolutionY; + + @SerializedName(ApiConstants.GPU_COUNT) + @Param(description = "the count of GPUs on the virtual machine", since = "4.21") + private Integer gpuCount; + @SerializedName(ApiConstants.BACKUP_OFFERING_ID) - @Param(description = "the ID of the backup offering of the virtual machine", since = "4.14") + @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("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) + @Param(description = "the VM's primary IP address") + 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) + @Param(description = "true if vm has delete protection.", since = "4.20.0") + private boolean deleteProtection; + @SerializedName(ApiConstants.SERVICE_STATE) @Param(description = "State of the Service from LB rule") 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) @@ -344,27 +401,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) @@ -375,6 +432,26 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co @Param(description = "VNF details", since = "4.19.0") private Map vnfDetails; + @SerializedName(ApiConstants.VM_TYPE) + @Param(description = "User VM type", since = "4.20.0") + private String vmType; + + @SerializedName(ApiConstants.ARCH) + @Param(description = "CPU arch of the VM", since = "4.20.1") + private String arch; + + @SerializedName(ApiConstants.INSTANCE_LEASE_DURATION) + @Param(description = "Instance lease duration in days", since = "4.21.0") + private Integer leaseDuration; + + @SerializedName(ApiConstants.INSTANCE_LEASE_EXPIRY_DATE) + @Param(description = "Instance lease expiry date", since = "4.21.0") + private Date leaseExpiryDate; + + @SerializedName(ApiConstants.INSTANCE_LEASE_EXPIRY_ACTION) + @Param(description = "Instance lease expiry action", since = "4.21.0") + private String leaseExpiryAction; + public UserVmResponse() { securityGroupList = new LinkedHashSet<>(); nics = new TreeSet<>(Comparator.comparingInt(x -> Integer.parseInt(x.getDeviceId()))); @@ -528,6 +605,42 @@ public String getDiskOfferingName() { return diskOfferingName; } + public String getGpuCardId() { + return gpuCardId; + } + + public String getGpuCardName() { + return gpuCardName; + } + + public String getVgpuProfileId() { + return vgpuProfileId; + } + + public String getVgpuProfileName() { + return vgpuProfileName; + } + + public Long getVideoRam() { + return videoRam; + } + + public Long getMaxHeads() { + return maxHeads; + } + + public Long getMaxResolutionX() { + return maxResolutionX; + } + + public Long getMaxResolutionY() { + return maxResolutionY; + } + + public Integer getGpuCount() { + return gpuCount; + } + public String getBackupOfferingId() { return backupOfferingId; } @@ -623,6 +736,10 @@ public String getHypervisor() { return hypervisor; } + public String getIpAddress() { + return ipAddress; + } + public String getPublicIpId() { return publicIpId; } @@ -690,6 +807,10 @@ public void setDomainName(String domainName) { this.domainName = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } public void setCreated(Date created) { this.created = created; } @@ -802,6 +923,42 @@ public void setDiskOfferingName(String diskOfferingName) { this.diskOfferingName = diskOfferingName; } + public void setGpuCardId(String gpuCardId) { + this.gpuCardId = gpuCardId; + } + + public void setGpuCardName(String gpuCardName) { + this.gpuCardName = gpuCardName; + } + + public void setVgpuProfileId(String vgpuProfileId) { + this.vgpuProfileId = vgpuProfileId; + } + + public void setVgpuProfileName(String vgpuProfileName) { + this.vgpuProfileName = vgpuProfileName; + } + + public void setVideoRam(Long videoRam) { + this.videoRam = videoRam; + } + + public void setMaxHeads(Long maxHeads) { + this.maxHeads = maxHeads; + } + + public void setMaxResolutionX(Long maxResolutionX) { + this.maxResolutionX = maxResolutionX; + } + + public void setMaxResolutionY(Long maxResolutionY) { + this.maxResolutionY = maxResolutionY; + } + + public void setGpuCount(Integer gpuCount) { + this.gpuCount = gpuCount; + } + public void setBackupOfferingId(String backupOfferingId) { this.backupOfferingId = backupOfferingId; } @@ -859,6 +1016,13 @@ public void setForVirtualNetwork(Boolean forVirtualNetwork) { public void setNics(Set nics) { this.nics = nics; + setIpAddress(nics); + } + + public void setIpAddress(final Set nics) { + if (CollectionUtils.isNotEmpty(nics)) { + this.ipAddress = nics.iterator().next().getIpaddress(); + } } public void addNic(NicResponse nic) { @@ -931,6 +1095,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; } @@ -955,6 +1123,10 @@ public String getReadOnlyDetails() { return readOnlyDetails; } + public String getAllowedDetails() { + return allowedDetails; + } + public Boolean getDynamicallyScalable() { return isDynamicallyScalable; } @@ -963,6 +1135,14 @@ public void setDynamicallyScalable(Boolean dynamicallyScalable) { isDynamicallyScalable = dynamicallyScalable; } + public boolean isDeleteProtection() { + return deleteProtection; + } + + public void setDeleteProtection(boolean deleteProtection) { + this.deleteProtection = deleteProtection; + } + public String getOsTypeId() { return osTypeId; } @@ -1076,6 +1256,14 @@ public void setTemplateType(String templateType) { this.templateType = templateType; } + public String getTemplateFormat() { + return templateFormat; + } + + public void setTemplateFormat(String templateFormat) { + this.templateFormat = templateFormat; + } + public List getVnfNics() { return vnfNics; } @@ -1105,4 +1293,49 @@ public void addVnfDetail(String key, String value) { } this.vnfDetails.put(key,value); } + + public void setVmType(String vmType) { + this.vmType = vmType; + } + + public String getVmType() { + return vmType; + } + + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } + + public String getArch() { + return arch; + } + + public void setArch(String arch) { + this.arch = arch; + } + + public Integer getLeaseDuration() { + return leaseDuration; + } + + public void setLeaseDuration(Integer leaseDuration) { + this.leaseDuration = leaseDuration; + } + + public String getLeaseExpiryAction() { + return leaseExpiryAction; + } + + public void setLeaseExpiryAction(String leaseExpiryAction) { + this.leaseExpiryAction = leaseExpiryAction; + } + + public Date getLeaseExpiryDate() { + return leaseExpiryDate; + } + + public void setLeaseExpiryDate(Date leaseExpiryDate) { + this.leaseExpiryDate = leaseExpiryDate; + } + } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VMScheduleResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VMScheduleResponse.java index 813a48e572c5..6800c25b0234 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 @@ -30,19 +30,19 @@ @EntityReference(value = VMSchedule.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) 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 9b553ed07444..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,83 +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") + 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() { @@ -261,6 +265,11 @@ public void setDomainName(String domainName) { this.domainName = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } + public void setTags(Set tags) { this.tags = tags; } 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 1b739e564421..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 = "Base 64 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/VgpuProfileResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VgpuProfileResponse.java new file mode 100644 index 000000000000..382b391ef592 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/VgpuProfileResponse.java @@ -0,0 +1,135 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.response; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.gpu.GpuCard; +import org.apache.cloudstack.gpu.VgpuProfile; + +@EntityReference(value = VgpuProfile.class) +public class VgpuProfileResponse extends GpuCardResponse { + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "the description of the vGPU profile") + private String description; + + @SerializedName(ApiConstants.GPU_CARD_ID) + @Param(description = "the ID of the GPU card associated with this vGPU profile") + private String gpuCardId; + + @SerializedName(ApiConstants.GPU_CARD_NAME) + @Param(description = "the name of the vGPU profile") + private String gpuCardName; + + @SerializedName(ApiConstants.MAX_VGPU_PER_PHYSICAL_GPU) + @Param(description = "the maximum number of vGPUs per physical GPU") + private Long maxVgpuPerPgpu; + + @SerializedName(ApiConstants.VIDEORAM) + @Param(description = "the video RAM size in MB") + private Long videoRam; + + @SerializedName(ApiConstants.MAXHEADS) + @Param(description = "the maximum number of display heads") + private Long maxHeads; + + @SerializedName(ApiConstants.MAXRESOLUTIONX) + @Param(description = "the maximum X resolution") + private Long maxResolutionX; + + @SerializedName(ApiConstants.MAXRESOLUTIONY) + @Param(description = "the maximum Y resolution") + private Long maxResolutionY; + + public VgpuProfileResponse(VgpuProfile vgpuProfile, GpuCard gpuCard) { + super(gpuCard); + id = vgpuProfile.getUuid(); + name = vgpuProfile.getName(); + description = vgpuProfile.getDescription(); + gpuCardId = gpuCard.getUuid(); + gpuCardName = gpuCard.getName(); + maxVgpuPerPgpu = vgpuProfile.getMaxVgpuPerPgpu(); + videoRam = vgpuProfile.getVideoRam(); + maxHeads = vgpuProfile.getMaxHeads(); + maxResolutionX = vgpuProfile.getMaxResolutionX(); + maxResolutionY = vgpuProfile.getMaxResolutionY(); + setObjectName("vgpuprofile"); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public String getGpuCardId() { + return gpuCardId; + } + + public String getGpuCardName() { + return gpuCardName; + } + + public Long getMaxVgpuPerPgpu() { + return maxVgpuPerPgpu; + } + + public void setDescription(String description) { + this.description = description; + } + + public Long getVideoRam() { + return videoRam; + } + + public void setVideoRam(Long videoRam) { + this.videoRam = videoRam; + } + + public Long getMaxHeads() { + return maxHeads; + } + + public void setMaxHeads(Long maxHeads) { + this.maxHeads = maxHeads; + } + + public Long getMaxResolutionX() { + return maxResolutionX; + } + + public void setMaxResolutionX(Long maxResolutionX) { + this.maxResolutionX = maxResolutionX; + } + + public Long getMaxResolutionY() { + return maxResolutionY; + } + + public void setMaxResolutionY(Long maxResolutionY) { + this.maxResolutionY = maxResolutionY; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/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/VirtualMachineResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VirtualMachineResponse.java new file mode 100644 index 000000000000..7d676292b8a0 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/VirtualMachineResponse.java @@ -0,0 +1,124 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.response; + +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; + +import com.cloud.serializer.Param; +import com.cloud.vm.VirtualMachine; +import com.google.gson.annotations.SerializedName; + +@EntityReference(value = VirtualMachine.class) +public class VirtualMachineResponse extends BaseResponse { + @SerializedName("id") + @Param(description = "the ID of the VM") + private String id; + + @SerializedName("type") + @Param(description = "the type of VM") + private String type; + + @SerializedName("name") + @Param(description = "the name of the VM") + private String name; + + @SerializedName("clusterid") + @Param(description = "the cluster ID for the VM") + private String clusterId; + + @SerializedName("clustername") + @Param(description = "the cluster name for the VM") + private String clusterName; + + @SerializedName("hostid") + @Param(description = "the host ID for the VM") + private String hostId; + + @SerializedName("hostname") + @Param(description = "the hostname for the VM") + private String hostName; + + @Override + public String getObjectId() { + return this.getId(); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getVmType() { + return type; + } + + public void setVmType(String type) { + this.type = type; + } + + public String getVmName() { + return name; + } + + public void setVmName(String name) { + this.name = name; + } + + public String getClusterId() { + return clusterId; + } + + public void setClusterId(String clusterId) { + this.clusterId = clusterId; + } + + public String getClusterName() { + return clusterName; + } + + public void setClusterName(String clusterName) { + this.clusterName = clusterName; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getHostId() { + return hostId; + } + + public void setHostId(String hostId) { + this.hostId = hostId; + } + + public String getHostName() { + return hostName; + } + + public void setHostName(String hostName) { + this.hostName = hostName; + } +} 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 c3b46710f6fa..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,25 +41,29 @@ 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") + private String domainPath; + @Override public void setAccountName(String accountName) { this.accountName = accountName; @@ -79,6 +83,10 @@ 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; 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 a22e2eb7024d..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,101 +28,109 @@ @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") + 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) + @Param(description = "indicates to which provider the IP range is dedicated to", since = "4.21.0") + private String provider; + public void setId(String id) { this.id = id; } @@ -162,6 +170,11 @@ public void setDomainName(String domainName) { this.domainName = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } + public void setPodId(String podId) { this.podId = podId; } @@ -235,4 +248,8 @@ public void setIp6Gateway(String ip6Gateway) { public void setIp6Cidr(String ip6Cidr) { this.ip6Cidr = ip6Cidr; } + + public void setProvider(String provider) { + this.provider = 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/VolumeForImportResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VolumeForImportResponse.java new file mode 100644 index 000000000000..803f154816a8 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/VolumeForImportResponse.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.api.response; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.storage.volume.VolumeOnStorageTO; + +import java.util.Map; + +@EntityReference(value = VolumeOnStorageTO.class) +public class VolumeForImportResponse extends BaseResponse { + + @SerializedName(ApiConstants.NAME) + @Param(description = "the name of the volume") + private String name; + + @SerializedName(ApiConstants.PATH) + @Param(description = "the path of the volume") + private String path; + + @SerializedName(ApiConstants.FULL_PATH) + @Param(description = "the full path of the volume") + private String fullPath; + + @SerializedName(ApiConstants.FORMAT) + @Param(description = "the format of the volume") + private String format; + + @SerializedName(ApiConstants.SIZE) + @Param(description = "the size of the volume") + private long size; + + @SerializedName(ApiConstants.VIRTUAL_SIZE) + @Param(description = "the virtual size of the volume") + private long virtualSize; + + @SerializedName(ApiConstants.ENCRYPT_FORMAT) + @Param(description = "the encrypt format of the volume") + private String qemuEncryptFormat; + + @SerializedName(ApiConstants.STORAGE_ID) + @Param(description = "id of the primary storage hosting the volume") + private String storagePoolId; + + @SerializedName(ApiConstants.STORAGE) + @Param(description = "name of the primary storage hosting the volume") + private String storagePoolName; + + @SerializedName(ApiConstants.STORAGE_TYPE) + @Param(description = "type of the primary storage hosting the volume") + private String storagePoolType; + + @SerializedName(ApiConstants.DETAILS) + @Param(description = "volume details in key/value pairs.") + private Map details; + + @SerializedName(ApiConstants.CHAIN_INFO) + @Param(description = "the chain info of the volume") + String chainInfo; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getFullPath() { + return fullPath; + } + + public void setFullPath(String fullPath) { + this.fullPath = fullPath; + } + + public String getFormat() { + return format; + } + + public void setFormat(String format) { + this.format = format; + } + + public long getSize() { + return size; + } + + public void setSize(long size) { + this.size = size; + } + + public long getVirtualSize() { + return virtualSize; + } + + public void setVirtualSize(long virtualSize) { + this.virtualSize = virtualSize; + } + + public String getQemuEncryptFormat() { + return qemuEncryptFormat; + } + + public void setQemuEncryptFormat(String qemuEncryptFormat) { + this.qemuEncryptFormat = qemuEncryptFormat; + } + + public String getStoragePoolId() { + return storagePoolId; + } + + public void setStoragePoolId(String storagePoolId) { + this.storagePoolId = storagePoolId; + } + + public String getStoragePoolName() { + return storagePoolName; + } + + public void setStoragePoolName(String storagePoolName) { + this.storagePoolName = storagePoolName; + } + + public String getStoragePoolType() { + return storagePoolType; + } + + public void setStoragePoolType(String storagePoolType) { + this.storagePoolType = storagePoolType; + } + + public Map getDetails() { + return details; + } + + public void setDetails(Map details) { + this.details = details; + } + + public String getChainInfo() { + return chainInfo; + } + + public void setChainInfo(String chainInfo) { + this.chainInfo = chainInfo; + } +} 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 00a1eabc40ba..058ea50f991e 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 @@ -18,6 +18,7 @@ import java.util.Date; import java.util.LinkedHashSet; +import java.util.Map; import java.util.Set; import org.apache.cloudstack.acl.RoleType; @@ -37,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) @@ -45,115 +46,119 @@ 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("vmstate") - @Param(description = "state of the virtual machine") + @SerializedName(ApiConstants.VIRTUAL_MACHINE_STATE) + @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) + @Param(description = "path of the Domain the disk volume belongs to", since = "4.19.2.0") + 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") @@ -165,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) @@ -189,105 +194,121 @@ 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") - private Boolean destroyed; + @Param(description = "The boolean state of whether the volume is destroyed or not") + private boolean destroyed; @SerializedName(ApiConstants.SERVICE_OFFERING_ID) @Param(description = "ID of the service offering for root disk") 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") - private Boolean extractable; + @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}) - private Boolean displayVolume; + @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) + @Param(description = "true if volume has delete protection.", since = "4.20.0") + private boolean deleteProtection; + @SerializedName(ApiConstants.PHYSICAL_SIZE) - @Param(description = "the bytes allocated") + @Param(description = "The bytes actually consumed on disk") private Long physicalsize; @SerializedName(ApiConstants.VIRTUAL_SIZE) - @Param(description = "the bytes actually consumed on disk") + @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) + @Param(description = "details for the volume check result, they may vary for different hypervisors", since = "4.19.1") + private Map volumeCheckResult; + + @SerializedName(ApiConstants.VOLUME_REPAIR_RESULT) + @Param(description = "details for the volume repair result, they may vary for different hypervisors", since = "4.19.1") + private Map volumeRepairResult; + + @SerializedName(ApiConstants.ENCRYPT_FORMAT) + @Param(description = "the format of the disk encryption if applicable", since = "4.19.1") + private String encryptionFormat; + public String getPath() { return path; } @@ -305,11 +326,11 @@ public String getObjectId() { return this.getId(); } - public Boolean isDestroyed() { + public boolean isDestroyed() { return destroyed; } - public void setDestroyed(Boolean destroyed) { + public void setDestroyed(boolean destroyed) { this.destroyed = destroyed; } @@ -396,6 +417,11 @@ public void setDomainName(String domainName) { this.domainName = domainName; } + @Override + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } + public void setStorageType(String storageType) { this.storageType = storageType; } @@ -508,7 +534,7 @@ public void setServiceOfferingDisplayText(String serviceOfferingDisplayText) { this.serviceOfferingDisplayText = serviceOfferingDisplayText; } - public void setExtractable(Boolean extractable) { + public void setExtractable(boolean extractable) { this.extractable = extractable; } @@ -526,7 +552,7 @@ public void setProjectName(String projectName) { this.projectName = projectName; } - public void setDisplayVolume(Boolean displayVm) { + public void setDisplayVolume(boolean displayVm) { this.displayVolume = displayVm; } @@ -562,6 +588,14 @@ public boolean getSupportsStorageSnapshot() { return this.supportsStorageSnapshot; } + public boolean isDeleteProtection() { + return deleteProtection; + } + + public void setDeleteProtection(boolean deleteProtection) { + this.deleteProtection = deleteProtection; + } + public String getIsoId() { return isoId; } @@ -742,7 +776,7 @@ public String getServiceOfferingDisplayText() { return serviceOfferingDisplayText; } - public Boolean getExtractable() { + public boolean isExtractable() { return extractable; } @@ -750,7 +784,7 @@ public String getStatus() { return status; } - public Boolean getDisplayVolume() { + public boolean isDisplayVolume() { return displayVolume; } @@ -817,4 +851,24 @@ public String getExternalUuid() { public void setExternalUuid(String externalUuid) { this.externalUuid = externalUuid; } + + public Map getVolumeCheckResult() { + return volumeCheckResult; + } + + public void setVolumeCheckResult(Map volumeCheckResult) { + this.volumeCheckResult = volumeCheckResult; + } + + public Map getVolumeRepairResult() { + return volumeRepairResult; + } + + public void setVolumeRepairResult(Map volumeRepairResult) { + this.volumeRepairResult = volumeRepairResult; + } + + public void setEncryptionFormat(String encryptionFormat) { + this.encryptionFormat = encryptionFormat; + } } 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 6881969646b2..a0516e660e48 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,61 +31,77 @@ @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") + private Boolean forNsx; + + @SerializedName(ApiConstants.NETWORK_MODE) + @Param(description = "Mode in which the network will operate. The valid values are NATTED and ROUTED") + 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") + private Boolean specifyAsNumber; + + @SerializedName(ApiConstants.ROUTING_MODE) + @Param(description = "The routing mode for the network offering, supported types are Static or Dynamic.") + private String routingMode; + public void setId(String id) { this.id = id; } @@ -138,6 +154,14 @@ public void setDomain(String domain) { this.domain = domain; } + public void setForNsx(Boolean forNsx) { + this.forNsx = forNsx; + } + + public void setNetworkMode(String networkMode) { + this.networkMode = networkMode; + } + public String getZoneId() { return zoneId; } @@ -161,4 +185,20 @@ public String getInternetProtocol() { public void setInternetProtocol(String internetProtocol) { this.internetProtocol = internetProtocol; } + + public Boolean getSpecifyAsNumber() { + return specifyAsNumber; + } + + public void setSpecifyAsNumber(Boolean specifyAsNumber) { + this.specifyAsNumber = specifyAsNumber; + } + + public String getRoutingMode() { + return routingMode; + } + + public void setRoutingMode(String routingMode) { + this.routingMode = routingMode; + } } 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 610416d7b0e3..2648ba836785 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 @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.response; import java.util.Date; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -33,99 +34,103 @@ @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.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") + 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) @@ -133,7 +138,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) @@ -141,21 +146,41 @@ 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) + @Param(description = "The IPv4 routing mode of VPC", since = "4.20.0") + private String ipv4Routing; + + @SerializedName(ApiConstants.IPV4_ROUTES) + @Param(description = "The routes for the VPC to ease adding route in upstream router", since = "4.20.0") + private Set ipv4Routes; + + @SerializedName(ApiConstants.AS_NUMBER_ID) + @Param(description = "UUID of AS NUMBER", since = "4.20.0") + private String asNumberId; + + @SerializedName(ApiConstants.AS_NUMBER) + @Param(description = "AS NUMBER", since = "4.20.0") + private Long asNumber; + + @SerializedName(ApiConstants.BGP_PEERS) + @Param(description = "The BGP peers for the VPC", since = "4.20.0") + private Set bgpPeers; + public void setId(final String id) { this.id = id; } @@ -209,6 +234,11 @@ public void setDomainName(final String domainName) { domain = domainName; } + @Override + public void setDomainPath(String path) { + this.domainPath = path; + } + public void setZoneId(final String zoneId) { this.zoneId = zoneId; } @@ -270,6 +300,18 @@ public void setResourceIconResponse(ResourceIconResponse icon) { this.icon = icon; } + public void setIpv4Routing(String ipv4Routing) { + this.ipv4Routing = ipv4Routing; + } + + public void setIpv4Routes(Set ipv4Routes) { + this.ipv4Routes = ipv4Routes; + } + + public void addIpv4Route(Ipv4RouteResponse ipv4Route) { + this.ipv4Routes.add(ipv4Route); + } + public void setIpv6Routes(Set ipv6Routes) { this.ipv6Routes = ipv6Routes; } @@ -297,4 +339,23 @@ public void setIpv6Dns1(String ipv6Dns1) { public void setIpv6Dns2(String ipv6Dns2) { this.ipv6Dns2 = ipv6Dns2; } + + public void setAsNumber(long asNumber) { + this.asNumber = asNumber; + } + + public void setAsNumberId(String asNumberId) { + this.asNumberId = asNumberId; + } + + public void setBgpPeers(Set bgpPeers) { + this.bgpPeers = bgpPeers; + } + + public void addBgpPeer(BgpPeerResponse bgpPeer) { + if (this.bgpPeers == null) { + this.setBgpPeers(new LinkedHashSet<>()); + } + this.bgpPeers.add(bgpPeer); + } } 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 d3e4d9416780..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,35 +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") + 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) { @@ -83,6 +87,11 @@ public void setDomainName(String name) { this.domainName = name; } + @Override + public void setDomainPath(String path) { + this.domainPath = path; + } + @Override public void setProjectId(String projectId) { this.projectId = projectId; 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 4e8e665836c3..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,59 +46,67 @@ 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") + @Param(description = "Total GPUs in the Zone", responseObject = Long.class, since = "4.21") + private Long gpuTotal; + + @SerializedName("gpuused") + @Param(description = "Used GPUs in the Zone", responseObject = Long.class, since = "4.21") + private Long gpuUsed; + @SerializedName("allocationstate") - @Param(description = "the allocation state of the cluster") + @Param(description = "The allocation state of the cluster") private String allocationState; @SerializedName(ApiConstants.ZONE_TOKEN) @@ -106,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) - private List capacitites; + @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) @@ -142,13 +150,43 @@ 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") + private boolean nsxEnabled = false; + + @SerializedName(ApiConstants.PROVIDER) + @Param(description = "External network provider if any", since = "4.21.0") + private String provider = null; + + @SerializedName(ApiConstants.MULTI_ARCH) + @Param(description = "True, if zone contains clusters and hosts from different CPU architectures", since = "4.20") + private boolean multiArch; + + @SerializedName(ApiConstants.ASN_RANGE) + @Param(description = "AS Number Range") + private String asnRange; + + @SerializedName(ApiConstants.ROUTED_MODE_ENABLED) + @Param(description = "True, if routed Network/VPC is enabled", since = "4.20.1") + private boolean routedModeEnabled = false; + + @SerializedName(ApiConstants.STORAGE_ACCESS_GROUPS) + @Param(description = "comma-separated list of storage access groups for the zone", since = "4.21.0") + private String storageAccessGroups; + + public ZoneResponse() { tags = new LinkedHashSet(); } + public ZoneResponse(Set tags) { + this.tags = tags; + } + public void setId(String id) { this.id = id; } @@ -197,10 +235,18 @@ public void setNetworkType(String networkType) { this.networkType = networkType; } - public void setSecurityGroupsEnabled(Boolean securityGroupsEnabled) { + public void setSecurityGroupsEnabled(boolean securityGroupsEnabled) { this.securityGroupsEnabled = securityGroupsEnabled; } + public void setGpuTotal(Long gpuTotal) { + this.gpuTotal = gpuTotal; + } + + public void setGpuUsed(Long gpuUsed) { + this.gpuUsed = gpuUsed; + } + public void setAllocationState(String allocationState) { this.allocationState = allocationState; } @@ -213,15 +259,15 @@ public void setDhcpProvider(String dhcpProvider) { this.dhcpProvider = dhcpProvider; } - public void setCapacitites(List capacitites) { - this.capacitites = capacitites; + public void setCapacities(List capacities) { + this.capacities = capacities; } public void setDomainName(String domainName) { this.domainName = domainName; } - public void setLocalStorageEnabled(Boolean localStorageEnabled) { + public void setLocalStorageEnabled(boolean localStorageEnabled) { this.localStorageEnabled = localStorageEnabled; } @@ -308,10 +354,6 @@ public String getNetworkType() { return networkType; } - public boolean isSecurityGroupsEnabled() { - return securityGroupsEnabled; - } - public String getAllocationState() { return allocationState; } @@ -324,12 +366,8 @@ public String getDhcpProvider() { return dhcpProvider; } - public List getCapacitites() { - return capacitites; - } - - public boolean isLocalStorageEnabled() { - return localStorageEnabled; + public List getCapacities() { + return capacities; } public Set getTags() { @@ -340,6 +378,46 @@ public Map getResourceDetails() { return resourceDetails; } + public boolean isSecurityGroupsEnabled() { + return securityGroupsEnabled; + } + + public Long getGpuUsed() { + return gpuUsed; + } + + public Long getGpuTotal() { + return gpuTotal; + } + + public boolean isLocalStorageEnabled() { + return localStorageEnabled; + } + + public Boolean getAllowUserSpecifyVRMtu() { + return allowUserSpecifyVRMtu; + } + + public Integer getRouterPrivateInterfaceMaxMtu() { + return routerPrivateInterfaceMaxMtu; + } + + public Integer getRouterPublicInterfaceMaxMtu() { + return routerPublicInterfaceMaxMtu; + } + + public boolean isNsxEnabled() { + return nsxEnabled; + } + + public String getProvider() { + return provider; + } + + public void setProvider(String provider) { + this.provider = provider; + } + @Override public void setResourceIconResponse(ResourceIconResponse resourceIconResponse) { this.resourceIconResponse = resourceIconResponse; @@ -368,4 +446,36 @@ public void setType(String type) { public String getType() { return type; } + + public String getStorageAccessGroups() { + return storageAccessGroups; + } + + public void setStorageAccessGroups(String storageAccessGroups) { + this.storageAccessGroups = storageAccessGroups; + } + + public void setNsxEnabled(boolean nsxEnabled) { + this.nsxEnabled = nsxEnabled; + } + + public void setMultiArch(boolean multiArch) { + this.multiArch = multiArch; + } + + public void setAsnRange(String asnRange) { + this.asnRange = asnRange; + } + + public String getAsnRange() { + return asnRange; + } + + public boolean isRoutedModeEnabled() { + return routedModeEnabled; + } + + public void setRoutedModeEnabled(boolean routedModeEnabled) { + this.routedModeEnabled = routedModeEnabled; + } } 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 f369367957d2..951af9180e7f 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/Backup.java +++ b/api/src/main/java/org/apache/cloudstack/backup/Backup.java @@ -18,6 +18,8 @@ package org.apache.cloudstack.backup; import java.util.Date; +import java.util.List; +import java.util.Map; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.api.Identity; @@ -62,6 +64,8 @@ class RestorePoint { private String id; private Date created; private String type; + private Long backupSize = 0L; + private Long dataSize = 0L; public RestorePoint(String id, Date created, String type) { this.id = id; @@ -69,6 +73,12 @@ public RestorePoint(String id, Date created, String type) { this.type = type; } + public RestorePoint(String id, Date created, String type, Long backupSize, Long dataSize) { + this(id, created, type); + this.backupSize = backupSize; + this.dataSize = dataSize; + } + public String getId() { return id; } @@ -92,6 +102,22 @@ public String getType() { public void setType(String type) { this.type = type; } + + public Long getBackupSize() { + return backupSize; + } + + public void setBackupSize(Long backupSize) { + this.backupSize = backupSize; + } + + public Long getDataSize() { + return dataSize; + } + + public void setDataSize(Long dataSize) { + this.dataSize = dataSize; + } } class VolumeInfo { @@ -99,12 +125,20 @@ class VolumeInfo { private Volume.Type type; private Long size; private String path; + private Long deviceId; + private String diskOfferingId; + private Long minIops; + private Long maxIops; - public VolumeInfo(String uuid, String path, Volume.Type type, Long size) { + public VolumeInfo(String uuid, String path, Volume.Type type, Long size, Long deviceId, String diskOfferingId, Long minIops, Long maxIops) { this.uuid = uuid; this.type = type; this.size = size; this.path = path; + this.deviceId = deviceId; + this.diskOfferingId = diskOfferingId; + this.minIops = minIops; + this.maxIops = maxIops; } public String getUuid() { @@ -127,18 +161,42 @@ public Long getSize() { return size; } + public Long getDeviceId() { + return deviceId; + } + + public String getDiskOfferingId() { + return diskOfferingId; + } + + public Long getMinIops() { + return minIops; + } + + public Long getMaxIops() { + return maxIops; + } + @Override public String toString() { - return StringUtils.join(":", uuid, path, type, size); + return StringUtils.join(":", uuid, path, type, size, deviceId, diskOfferingId, minIops, maxIops); } } - long getVmId(); + Long getVmId(); + long getBackupOfferingId(); String getExternalId(); String getType(); Date getDate(); Backup.Status getStatus(); Long getSize(); Long getProtectedSize(); + void setName(String name); + String getDescription(); + void setDescription(String description); + List getBackedUpVolumes(); long getZoneId(); + Map getDetails(); + String getDetail(String name); + Long getBackupScheduleId(); } diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java index 7b39804c738e..e83db3a25895 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java @@ -18,18 +18,31 @@ package org.apache.cloudstack.backup; import java.util.List; +import java.util.Map; +import com.cloud.capacity.Capacity; +import com.cloud.exception.ResourceAllocationException; import org.apache.cloudstack.api.command.admin.backup.ImportBackupOfferingCmd; import org.apache.cloudstack.api.command.admin.backup.UpdateBackupOfferingCmd; +import org.apache.cloudstack.api.command.user.backup.CreateBackupCmd; import org.apache.cloudstack.api.command.user.backup.CreateBackupScheduleCmd; +import org.apache.cloudstack.api.command.user.backup.DeleteBackupScheduleCmd; import org.apache.cloudstack.api.command.user.backup.ListBackupOfferingsCmd; +import org.apache.cloudstack.api.command.user.backup.ListBackupScheduleCmd; import org.apache.cloudstack.api.command.user.backup.ListBackupsCmd; +import org.apache.cloudstack.api.response.BackupResponse; import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.ValidatedConfigKey; import org.apache.cloudstack.framework.config.Configurable; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.storage.Volume; import com.cloud.utils.Pair; import com.cloud.utils.component.Manager; import com.cloud.utils.component.PluggableService; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VmDiskInfo; /** * Backup and Recover Manager Interface @@ -41,10 +54,11 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer "false", "Is backup and recovery framework enabled.", false, ConfigKey.Scope.Zone); - ConfigKey BackupProviderPlugin = new ConfigKey<>("Advanced", String.class, + ConfigKey BackupProviderPlugin = new ValidatedConfigKey<>("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. Valid plugin values: dummy, veeam, networker and nas", + true, ConfigKey.Scope.Zone, BackupFrameworkEnabled.key(), value -> validateBackupProviderConfig((String)value)); ConfigKey BackupSyncPollingInterval = new ConfigKey<>("Advanced", Long.class, "backup.framework.sync.interval", @@ -56,6 +70,62 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer "false", "Enable volume attach/detach operations for VMs that are assigned to Backup Offerings.", true); + ConfigKey DefaultMaxAccountBackups = new ConfigKey("Account Defaults", Long.class, + "max.account.backups", + "20", + "The default maximum number of backups that can be created for an account", + false, + ConfigKey.Scope.Global, + null); + + ConfigKey DefaultMaxAccountBackupStorage = new ConfigKey("Account Defaults", Long.class, + "max.account.backup.storage", + "400", + "The default maximum backup storage space (in GiB) that can be used for an account", + false, + ConfigKey.Scope.Global, + null); + + ConfigKey DefaultMaxProjectBackups = new ConfigKey("Project Defaults", Long.class, + "max.project.backups", + "20", + "The default maximum number of backups that can be created for a project", + false, + ConfigKey.Scope.Global, + null); + + ConfigKey DefaultMaxProjectBackupStorage = new ConfigKey("Project Defaults", Long.class, + "max.project.backup.storage", + "400", + "The default maximum backup storage space (in GiB) that can be used for a project", + false, + ConfigKey.Scope.Global, + null); + + ConfigKey DefaultMaxDomainBackups = new ConfigKey("Domain Defaults", Long.class, + "max.domain.backups", + "40", + "The default maximum number of backups that can be created for a domain", + false, + ConfigKey.Scope.Global, + null); + + ConfigKey DefaultMaxDomainBackupStorage = new ConfigKey("Domain Defaults", Long.class, + "max.domain.backup.storage", + "800", + "The default maximum backup storage space (in GiB) that can be used for a domain", + false, + ConfigKey.Scope.Global, + null); + + ConfigKey BackupStorageCapacityThreshold = new ConfigKey<>("Alert", Float.class, + "zone.backupStorage.capacity.notificationthreshold", + "0.75", + "Percentage (as a value between 0 and 1) of backup storage utilization above which alerts will be sent about low storage available.", + true, + ConfigKey.Scope.Zone, + null); + /** * List backup provider offerings * @param zoneId zone id @@ -68,6 +138,8 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer */ BackupOffering importBackupOffering(final ImportBackupOfferingCmd cmd); + List getBackupOfferingDomains(final Long offeringId); + /** * List backup offerings * @param ListBackupOfferingsCmd API cmd @@ -107,21 +179,22 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer * @param vmId * @return */ - BackupSchedule listBackupSchedule(Long vmId); + List listBackupSchedules(ListBackupScheduleCmd cmd); /** * Deletes VM backup schedule for a VM - * @param vmId + * @param cmd * @return */ - boolean deleteBackupSchedule(Long vmId); + boolean deleteBackupSchedule(DeleteBackupScheduleCmd cmd); /** * Creates backup of a VM - * @param vmId Virtual Machine ID + * @param cmd CreateBackupCmd + * @param job The async job associated with the backup retention * @return returns operation success */ - boolean createBackup(final Long vmId); + boolean createBackup(CreateBackupCmd cmd, Object job) throws ResourceAllocationException; /** * List existing backups for a VM @@ -133,6 +206,17 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer */ boolean restoreBackup(final Long backupId); + 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; + /** * Restore a backed up volume and attach it to a VM */ @@ -146,5 +230,35 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer */ boolean deleteBackup(final Long backupId, final Boolean forced); + void validateBackupForZone(Long zoneId); + BackupOffering updateBackupOffering(UpdateBackupOfferingCmd updateBackupOfferingCmd); + + VmDiskInfo getRootDiskInfoFromBackup(Backup backup); + + List getDataDiskInfoListFromBackup(Backup backup); + + void checkVmDisksSizeAgainstBackup(List vmDiskInfoList, Backup backup); + + Map getBackupDetailsFromVM(VirtualMachine vm); + + String createVolumeInfoFromVolumes(List vmVolumes); + + String getBackupNameFromVM(VirtualMachine vm); + + BackupResponse createBackupResponse(Backup backup, Boolean listVmDetails); + + Capacity getBackupStorageUsedStats(Long zoneId); + + void checkAndRemoveBackupOfferingBeforeExpunge(VirtualMachine vm); + + static void validateBackupProviderConfig(String value) { + if (value != null && (value.contains(",") || value.trim().contains(" "))) { + throw new IllegalArgumentException("Multiple backup provider plugins are not supported. Please provide a single plugin value."); + } + List validPlugins = List.of("dummy", "veeam", "networker", "nas"); + if (value != null && !validPlugins.contains(value)) { + throw new IllegalArgumentException("Invalid backup provider plugin: " + value + ". Valid plugin values are: " + String.join(", ", validPlugins)); + } + } } 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 9c1b14ae60f2..23b8092425d9 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java @@ -17,13 +17,14 @@ package org.apache.cloudstack.backup; import java.util.List; -import java.util.Map; import com.cloud.utils.Pair; import com.cloud.vm.VirtualMachine; public interface BackupProvider { + Boolean crossZoneInstanceCreationEnabled(BackupOffering backupOffering); + /** * Returns the unique name of the provider * @return returns provider name @@ -49,22 +50,21 @@ public interface BackupProvider { /** * Assign a VM to a backup offering or policy - * @param vm - * @param backup - * @param policy - * @return + * @param vm the machine to back up + * @param backupOffering the SLA definition for the backup + * @return succeeded? */ boolean assignVMToBackupOffering(VirtualMachine vm, BackupOffering backupOffering); /** * Removes a VM from a backup offering or policy - * @param vm - * @return + * @param vm the machine to stop backing up + * @return succeeded? */ boolean removeVMFromBackupOffering(VirtualMachine vm); /** - * Whether the provide will delete backups on removal of VM from the offfering + * Whether the provider will delete backups on removal of VM from the offering * @return boolean result */ boolean willDeleteBackupsOnOfferingRemoval(); @@ -72,19 +72,23 @@ public interface BackupProvider { /** * Starts and creates an adhoc backup process * for a previously registered VM backup - * @param backup - * @return + * + * @param vm the machine to make a backup of + * @param quiesceVM instance will be quiesced for checkpointing for backup. Applicable only to NAS plugin. + * @return the result and {code}Backup{code} {code}Object{code} */ - boolean takeBackup(VirtualMachine vm); + Pair takeBackup(VirtualMachine vm, Boolean quiesceVM); /** * Delete an existing backup - * @param backuo The backup to exclude + * @param backup The backup to exclude * @param forced Indicates if backup will be force removed or not - * @return + * @return succeeded? */ boolean deleteBackup(Backup backup, boolean forced); + Pair restoreBackupToVM(VirtualMachine vm, Backup backup, String hostIp, String dataStoreUuid); + /** * Restore VM from backup */ @@ -93,20 +97,48 @@ public interface BackupProvider { /** * Restore a volume from a backup */ - Pair restoreBackedUpVolume(Backup backup, String volumeUuid, String hostIp, String dataStoreUuid); + Pair restoreBackedUpVolume(Backup backup, Backup.VolumeInfo backupVolumeInfo, String hostIp, String dataStoreUuid, Pair vmNameAndState); + + /** + * Syncs backup metrics (backup size, protected size) from the plugin and stores it within the provider + * @param zoneId the zone for which to return metrics + */ + void syncBackupMetrics(Long zoneId); + + /** + * Returns a list of Backup.RestorePoint + * @param vm the machine to get the restore points for + */ + List listRestorePoints(VirtualMachine vm); /** - * Returns backup metrics for a list of VMs in a zone - * @param zoneId - * @param vms - * @return + * Creates and returns an entry in the backups table by getting the information from restorePoint and vm. + * + * @param restorePoint the restore point to create a backup for + * @param vm The machine for which to create a backup */ - Map getBackupMetrics(Long zoneId, List vms); + Backup createNewBackupEntryForRestorePoint(Backup.RestorePoint restorePoint, VirtualMachine vm); /** - * This method should reconcile and create backup entries for any backups created out-of-band - * @param vm - * @param metric + * Returns if the backup provider supports creating new instance from backup */ - void syncBackups(VirtualMachine vm, Backup.Metric metric); + 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 + * @return a pair of Used size and Total size for the backup storage + */ + Pair getBackupStorageStats(Long zoneId); + + /** + * Gets the backup storage usage (Used, Total) from the plugin and stores it in db + * @param zoneId the zone for which to return metrics + */ + void syncBackupStorageStats(Long zoneId); + } diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupRepository.java b/api/src/main/java/org/apache/cloudstack/backup/BackupRepository.java new file mode 100644 index 000000000000..886d13c13f93 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupRepository.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; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +public interface BackupRepository extends InternalIdentity, Identity { + String getProvider(); + long getZoneId(); + String getName(); + 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 new file mode 100644 index 000000000000..cc8144ebe403 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupRepositoryService.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.backup; + +import com.cloud.utils.Pair; +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 d81dd731b1ff..44fdf70c4c15 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupSchedule.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupSchedule.java @@ -19,15 +19,19 @@ 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(); String getTimezone(); Date getScheduledTimestamp(); Long getAsyncJobId(); + Boolean getQuiesceVM(); + int getMaxBackups(); + String getUuid(); } 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/api/src/main/java/org/apache/cloudstack/ca/CAManager.java b/api/src/main/java/org/apache/cloudstack/ca/CAManager.java index 12a9d3d7b41c..b0fb1ac73c21 100644 --- a/api/src/main/java/org/apache/cloudstack/ca/CAManager.java +++ b/api/src/main/java/org/apache/cloudstack/ca/CAManager.java @@ -77,6 +77,14 @@ public interface CAManager extends CAService, Configurable, PluggableService { "15", "The number of days before expiry of a client certificate, the validations are checked. Admins are alerted when auto-renewal is not allowed, otherwise auto-renewal is attempted.", true, ConfigKey.Scope.Cluster); + + ConfigKey CertManagementCustomSubjectAlternativeName = new ConfigKey<>("Advanced", String.class, + "ca.framework.cert.management.custom.san", + "cloudstack.internal", + "The custom Subject Alternative Name that will be added to the management server certificate. " + + "The actual implementation will depend on the configured CA provider.", + false); + /** * Returns a list of available CA provider plugins * @return returns list of CAProvider 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 15f7fcd81741..368487c2b9b0 100644 --- a/api/src/main/java/org/apache/cloudstack/cluster/ClusterDrsAlgorithm.java +++ b/api/src/main/java/org/apache/cloudstack/cluster/ClusterDrsAlgorithm.java @@ -21,10 +21,11 @@ import com.cloud.host.Host; import com.cloud.offering.ServiceOffering; -import com.cloud.utils.Pair; +import com.cloud.org.Cluster; 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; @@ -39,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. @@ -55,82 +59,124 @@ public interface ClusterDrsAlgorithm extends Adapter { * @throws ConfigurationException * if there is an error in the configuration */ - boolean needsDrs(long clusterId, List> cpuList, - List> memoryList) throws ConfigurationException; - + 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(long clusterId, VirtualMachine vm, ServiceOffering serviceOffering, + 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, @@ -150,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) { @@ -180,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/command/ReconcileCommandService.java b/api/src/main/java/org/apache/cloudstack/command/ReconcileCommandService.java new file mode 100644 index 000000000000..89ab97990df7 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/command/ReconcileCommandService.java @@ -0,0 +1,65 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.command; + + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.hypervisor.Hypervisor; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.framework.config.ConfigKey; + +import java.util.Arrays; +import java.util.List; + +public interface ReconcileCommandService { + + ConfigKey ReconcileCommandsEnabled = new ConfigKey<>("Advanced", Boolean.class, + "reconcile.commands.enabled", "false", + "Indicates whether the background task to reconcile the commands is enabled or not", + false); + + ConfigKey ReconcileCommandsInterval = new ConfigKey<>("Advanced", Integer.class, + "reconcile.commands.interval", "60", + "Interval (in seconds) for the background task to reconcile the commands", + false); + ConfigKey ReconcileCommandsMaxAttempts = new ConfigKey<>("Advanced", Integer.class, + "reconcile.commands.max.attempts", "30", + "The maximum number of attempts to reconcile the commands", + true); + + ConfigKey ReconcileCommandsWorkers = new ConfigKey<>("Advanced", Integer.class, + "reconcile.commands.workers", "100", + "The Number of worker threads to reconcile the commands", + false); + + List SupportedHypervisorTypes = Arrays.asList(Hypervisor.HypervisorType.KVM); + + void persistReconcileCommands(Long hostId, Long requestSequence, Command[] cmd); + + boolean updateReconcileCommand(long requestSeq, Command command, Answer answer, Command.State newStateByManagement, Command.State newStateByAgent); + + void processCommand(Command pingCommand, Answer pingAnswer); + + void processAnswers(long requestSeq, Command[] commands, Answer[] answers); + + void updateReconcileCommandToInterruptedByManagementServerId(long managementServerId); + + void updateReconcileCommandToInterruptedByHostId(long hostId); + + boolean isReconcileResourceNeeded(long resourceId, ApiCommandResourceType resourceType); +} diff --git a/api/src/main/java/org/apache/cloudstack/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/consoleproxy/ConsoleAccessManager.java b/api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManager.java index 5bd9699b2019..655b8faf443a 100644 --- a/api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManager.java +++ b/api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManager.java @@ -18,6 +18,9 @@ import com.cloud.utils.component.Manager; import org.apache.cloudstack.api.command.user.consoleproxy.ConsoleEndpoint; +import org.apache.cloudstack.api.command.user.consoleproxy.ListConsoleSessionsCmd; +import org.apache.cloudstack.api.response.ConsoleSessionResponse; +import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; import java.util.Date; @@ -44,8 +47,12 @@ public interface ConsoleAccessManager extends Manager, Configurable { void removeSessions(String[] sessionUuids); - void acquireSession(String sessionUuid); + void acquireSession(String sessionUuid, String clientAddress); String genAccessTicket(String host, String port, String sid, String tag, String sessionUuid); String genAccessTicket(String host, String port, String sid, String tag, Date normalizedHashTime, String sessionUuid); + + ListResponse listConsoleSessions(ListConsoleSessionsCmd cmd); + + ConsoleSession listConsoleSessionById(long id); } diff --git a/api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleSession.java b/api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleSession.java new file mode 100644 index 000000000000..6cbdd31fd943 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleSession.java @@ -0,0 +1,45 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.consoleproxy; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +public interface ConsoleSession extends InternalIdentity, Identity { + + Date getCreated(); + + long getDomainId(); + + long getAccountId(); + + long getUserId(); + + long getInstanceId(); + + long getHostId(); + + Date getRemoved(); + + Date getAcquired(); + + String getConsoleEndpointCreatorAddress(); + + String getClientAddress(); +} diff --git a/api/src/main/java/org/apache/cloudstack/context/CallContext.java b/api/src/main/java/org/apache/cloudstack/context/CallContext.java index 69376e4f6d7d..5e0c60184f4f 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 UUID getApiResourceUuid(String paramName) { + return apiResourcesUuids.get(paramName); + } + + public void putApiResourceUuid(String paramName, UUID 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/datacenter/DataCenterIpv4GuestSubnet.java b/api/src/main/java/org/apache/cloudstack/datacenter/DataCenterIpv4GuestSubnet.java new file mode 100644 index 000000000000..90d55cc57513 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/datacenter/DataCenterIpv4GuestSubnet.java @@ -0,0 +1,36 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.datacenter; + +import java.util.Date; + +import org.apache.cloudstack.acl.InfrastructureEntity; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +public interface DataCenterIpv4GuestSubnet extends InfrastructureEntity, InternalIdentity, Identity { + Long getDataCenterId(); + + String getSubnet(); + + Long getDomainId(); + + Long getAccountId(); + + Date getCreated(); +} diff --git a/api/src/main/java/org/apache/cloudstack/dedicated/DedicatedResourceResponse.java b/api/src/main/java/org/apache/cloudstack/dedicated/DedicatedResourceResponse.java new file mode 100644 index 000000000000..fc116cbb91e3 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/dedicated/DedicatedResourceResponse.java @@ -0,0 +1,44 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.dedicated; + +import com.cloud.dc.DedicatedResources; +import com.cloud.serializer.Param; + +import com.google.gson.annotations.SerializedName; + +import org.apache.cloudstack.api.BaseResponse; + +public class DedicatedResourceResponse extends BaseResponse { + @SerializedName("resourceid") + @Param(description = "the ID of the resource") + private String resourceId; + + @SerializedName("resourcename") + @Param(description = "the name of the resource") + private String resourceName; + + @SerializedName("resourcetype") + @Param(description = "the type of the resource") + private DedicatedResources.Type resourceType; + + public DedicatedResourceResponse(String resourceId, String resourceName, DedicatedResources.Type resourceType) { + this.resourceId = resourceId; + this.resourceName = resourceName; + this.resourceType = resourceType; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/extension/CustomActionResultResponse.java b/api/src/main/java/org/apache/cloudstack/extension/CustomActionResultResponse.java new file mode 100644 index 000000000000..33ff70fcace5 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/extension/CustomActionResultResponse.java @@ -0,0 +1,65 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.extension; + +import java.util.Map; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class CustomActionResultResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the action") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "Name of the action") + private String name; + + @SerializedName(ApiConstants.SUCCESS) + @Param(description = "Whether custom action succeed or not") + private Boolean success; + + @SerializedName(ApiConstants.RESULT) + @Param(description = "Result of the action execution") + private Map result; + + public void setId(String id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + public void setSuccess(Boolean success) { + this.success = success; + } + + public Boolean getSuccess() { + return success; + } + + public void setResult(Map result) { + this.result = result; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/extension/Extension.java b/api/src/main/java/org/apache/cloudstack/extension/Extension.java new file mode 100644 index 000000000000..3068612ed6fe --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/extension/Extension.java @@ -0,0 +1,44 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//with the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +//KIND, either express or implied. See the License for the +//specific language governing permissions and limitations +//under the License. + +package org.apache.cloudstack.extension; + +import java.util.Date; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +public interface Extension extends InternalIdentity, Identity { + enum Type { + Orchestrator + } + enum State { + Enabled, Disabled; + }; + String getName(); + String getDescription(); + Type getType(); + String getRelativePath(); + boolean isPathReady(); + boolean isUserDefined(); + State getState(); + Date getCreated(); + + static String getDirectoryName(String name) { + return name.replaceAll("[^a-zA-Z0-9._-]", "_").toLowerCase(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/extension/ExtensionCustomAction.java b/api/src/main/java/org/apache/cloudstack/extension/ExtensionCustomAction.java new file mode 100644 index 000000000000..776b42f671b7 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/extension/ExtensionCustomAction.java @@ -0,0 +1,386 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//with the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +//KIND, either express or implied. See the License for the +//specific language governing permissions and limitations +//under the License. + +package org.apache.cloudstack.extension; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.StringUtils; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.utils.DateUtil; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; + +public interface ExtensionCustomAction extends InternalIdentity, Identity { + enum ResourceType { + VirtualMachine(com.cloud.vm.VirtualMachine.class); + + private final Class clazz; + + ResourceType(Class clazz) { + this.clazz = clazz; + } + + public Class getAssociatedClass() { + return this.clazz; + } + } + + String getName(); + + String getDescription(); + + long getExtensionId(); + + ResourceType getResourceType(); + + Integer getAllowedRoleTypes(); + + String getSuccessMessage(); + + String getErrorMessage(); + + int getTimeout(); + + boolean isEnabled(); + + Date getCreated(); + + + class Parameter { + + public enum Type { + STRING(true), + NUMBER(true), + BOOLEAN(false), + DATE(false); + + private final boolean supportsOptions; + + Type(boolean supportsOptions) { + this.supportsOptions = supportsOptions; + } + + public boolean canSupportsOptions() { + return supportsOptions; + } + } + + public enum ValidationFormat { + // Universal default format + NONE(null), + + // String formats + UUID(Type.STRING), + EMAIL(Type.STRING), + PASSWORD(Type.STRING), + URL(Type.STRING), + + // Number formats + DECIMAL(Type.NUMBER); + + private final Type baseType; + + ValidationFormat(Type baseType) { + this.baseType = baseType; + } + + public Type getBaseType() { + return baseType; + } + } + + private static final Gson gson = new GsonBuilder() + .registerTypeAdapter(Parameter.class, new ParameterDeserializer()) + .setPrettyPrinting() + .create(); + + private final String name; + private final Type type; + private final ValidationFormat validationformat; + private final List valueoptions; + private final boolean required; + + public Parameter(String name, Type type, ValidationFormat validationformat, List valueoptions, boolean required) { + this.name = name; + this.type = type; + this.validationformat = validationformat; + this.valueoptions = valueoptions; + this.required = required; + } + + /** + * Parses a CSV string into a list of validated options. + */ + private static List parseValueOptions(String name, String csv, Type parsedType, ValidationFormat parsedFormat) { + if (StringUtils.isBlank(csv)) { + return null; + } + List values = Arrays.stream(csv.split(",")) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .collect(Collectors.toList()); + switch (parsedType) { + case STRING: + if (parsedFormat != null && parsedFormat != ValidationFormat.NONE) { + for (String value : values) { + if (!isValidStringValue(value, parsedFormat)) { + throw new InvalidParameterValueException(String.format("Invalid value options with validation format: %s for parameter: %s", parsedFormat.name(), name)); + } + } + } + return new ArrayList<>(values); + case NUMBER: + try { + return values.stream() + .map(v -> parseNumber(v, parsedFormat)) + .collect(Collectors.toList()); + } catch (NumberFormatException ignored) { + throw new InvalidParameterValueException(String.format("Invalid value options with validation format: %s for parameter: %s", parsedFormat.name(), name)); + } + default: + throw new InvalidParameterValueException(String.format("Options not supported for type: %s for parameter: %s", parsedType, name)); + } + } + + private static Object parseNumber(String value, ValidationFormat parsedFormat) { + if (parsedFormat == ValidationFormat.DECIMAL) { + return Float.parseFloat(value); + } + return Integer.parseInt(value); + } + + private static boolean isValidStringValue(String value, ValidationFormat validationFormat ) { + switch (validationFormat) { + case NONE: + return true; + case UUID: + try { + UUID.fromString(value); + return true; + } catch (Exception ignored) { + return false; + } + case EMAIL: + return value.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$"); + case PASSWORD: + return !value.trim().isEmpty(); + case URL: + try { + new java.net.URL(value); + return true; + } catch (Exception ignored) { + return false; + } + default: + return false; + } + } + + public static Parameter fromMap(Map map) throws InvalidParameterValueException { + final String name = map.get(ApiConstants.NAME); + final String typeStr = map.get(ApiConstants.TYPE); + final String validationFormatStr = map.get(ApiConstants.VALIDATION_FORMAT); + final String required = map.get(ApiConstants.REQUIRED); + final String valueOptionsStr = map.get(ApiConstants.VALUE_OPTIONS); + if (StringUtils.isBlank(name)) { + throw new InvalidParameterValueException("Invalid parameter specified with empty name"); + } + if (StringUtils.isBlank(typeStr)) { + throw new InvalidParameterValueException(String.format("No type specified for parameter: %s", name)); + } + Type parsedType = EnumUtils.getEnumIgnoreCase(Type.class, typeStr); + if (parsedType == null) { + throw new InvalidParameterValueException(String.format("Invalid type: %s specified for parameter: %s", + typeStr, name)); + } + ValidationFormat parsedFormat = StringUtils.isBlank(validationFormatStr) ? + ValidationFormat.NONE : EnumUtils.getEnumIgnoreCase(ValidationFormat.class, validationFormatStr); + if (parsedFormat == null || (!ValidationFormat.NONE.equals(parsedFormat) && + parsedFormat.getBaseType() != parsedType)) { + throw new InvalidParameterValueException( + String.format("Invalid validation format: %s specified for type: %s", + parsedFormat, parsedType.name())); + } + List valueOptions = parseValueOptions(name, valueOptionsStr, parsedType, parsedFormat); + return new Parameter(name, parsedType, parsedFormat, valueOptions, Boolean.parseBoolean(required)); + } + + public String getName() { + return name; + } + + public Type getType() { + return type; + } + + public ValidationFormat getValidationFormat() { + return validationformat; + } + + public List getValueOptions() { + return valueoptions; + } + + public boolean isRequired() { + return required; + } + + @Override + public String toString() { + return String.format("Parameter %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, + ApiConstants.NAME, ApiConstants.TYPE, ApiConstants.REQUIRED)); + } + + public static String toJsonFromList(List parameters) { + return gson.toJson(parameters); + } + + public static List toListFromJson(String json) { + java.lang.reflect.Type listType = new TypeToken>() {}.getType(); + return gson.fromJson(json, listType); + } + + private void validateValueInOptions(Object value) { + if (CollectionUtils.isNotEmpty(valueoptions) && !valueoptions.contains(value)) { + throw new InvalidParameterValueException("Invalid value for parameter '" + name + "': " + value + + ". Valid options are: " + valueoptions.stream().map(Object::toString).collect(Collectors.joining(", "))); + } + } + + public Object validatedValue(String value) { + if (StringUtils.isBlank(value)) { + throw new InvalidParameterValueException("Empty value for parameter '" + name + "': " + value); + } + try { + switch (type) { + case BOOLEAN: + if (!Arrays.asList("true", "false").contains(value)) { + throw new IllegalArgumentException(); + } + return Boolean.parseBoolean(value); + case DATE: + return DateUtil.parseTZDateString(value); + case NUMBER: + Object obj = parseNumber(value, validationformat); + validateValueInOptions(obj); + return obj; + default: + if (!isValidStringValue(value, validationformat)) { + throw new IllegalArgumentException(); + } + validateValueInOptions(value); + return value; + } + } catch (Exception e) { + throw new InvalidParameterValueException("Invalid value for parameter '" + name + "': " + value); + } + } + + public static Map validateParameterValues(List parameterDefinitions, + Map suppliedValues) throws InvalidParameterValueException { + if (suppliedValues == null) { + suppliedValues = new HashMap<>(); + } + for (Parameter param : parameterDefinitions) { + String value = suppliedValues.get(param.getName()); + if (param.isRequired()) { + if (value == null || value.trim().isEmpty()) { + throw new InvalidParameterValueException("Missing or empty required parameter: " + param.getName()); + } + } + } + Map validatedParams = new HashMap<>(); + for (Map.Entry entry : suppliedValues.entrySet()) { + String name = entry.getKey(); + String value = entry.getValue(); + Parameter param = parameterDefinitions.stream() + .filter(p -> p.getName().equals(name)) + .findFirst() + .orElse(null); + if (param != null) { + validatedParams.put(name, param.validatedValue(value)); + } else { + validatedParams.put(name, value); + } + } + return validatedParams; + } + + static class ParameterDeserializer implements JsonDeserializer { + + @Override + public Parameter deserialize(JsonElement json, java.lang.reflect.Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + JsonObject obj = json.getAsJsonObject(); + String name = obj.get(ApiConstants.NAME).getAsString(); + String typeStr = obj.get(ApiConstants.TYPE).getAsString(); + String validationFormatStr = obj.has(ApiConstants.VALIDATION_FORMAT) ? obj.get(ApiConstants.VALIDATION_FORMAT).getAsString() : null; + boolean required = obj.has(ApiConstants.REQUIRED) && obj.get(ApiConstants.REQUIRED).getAsBoolean(); + + Parameter.Type typeEnum = Parameter.Type.valueOf(typeStr); + Parameter.ValidationFormat validationFormatEnum = (validationFormatStr != null) + ? Parameter.ValidationFormat.valueOf(validationFormatStr) + : Parameter.ValidationFormat.NONE; + + List valueOptions = null; + if (obj.has(ApiConstants.VALUE_OPTIONS) && obj.get(ApiConstants.VALUE_OPTIONS).isJsonArray()) { + JsonArray valueOptionsArray = obj.getAsJsonArray(ApiConstants.VALUE_OPTIONS); + valueOptions = new ArrayList<>(); + for (JsonElement el : valueOptionsArray) { + switch (typeEnum) { + case STRING: + valueOptions.add(el.getAsString()); + break; + case NUMBER: + if (validationFormatEnum == Parameter.ValidationFormat.DECIMAL) { + valueOptions.add(el.getAsFloat()); + } else { + valueOptions.add(el.getAsInt()); + } + break; + default: + throw new JsonParseException("Unsupported type for value options"); + } + } + } + + return new Parameter(name, typeEnum, validationFormatEnum, valueOptions, required); + } + } + } +} diff --git a/api/src/main/java/org/apache/cloudstack/extension/ExtensionHelper.java b/api/src/main/java/org/apache/cloudstack/extension/ExtensionHelper.java new file mode 100644 index 000000000000..f50f841ed74a --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/extension/ExtensionHelper.java @@ -0,0 +1,24 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.extension; + +public interface ExtensionHelper { + Long getExtensionIdForCluster(long clusterId); + Extension getExtension(long id); + Extension getExtensionForCluster(long clusterId); +} diff --git a/api/src/main/java/org/apache/cloudstack/extension/ExtensionResourceMap.java b/api/src/main/java/org/apache/cloudstack/extension/ExtensionResourceMap.java new file mode 100644 index 000000000000..40ebc19eb5e3 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/extension/ExtensionResourceMap.java @@ -0,0 +1,34 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//with the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +//KIND, either express or implied. See the License for the +//specific language governing permissions and limitations +//under the License. + +package org.apache.cloudstack.extension; + +import java.util.Date; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +public interface ExtensionResourceMap extends InternalIdentity, Identity { + enum ResourceType { + Cluster + } + + long getExtensionId(); + long getResourceId(); + ResourceType getResourceType(); + Date getCreated(); +} diff --git a/api/src/main/java/org/apache/cloudstack/gpu/GpuCard.java b/api/src/main/java/org/apache/cloudstack/gpu/GpuCard.java new file mode 100644 index 000000000000..2c02a0e30c26 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gpu/GpuCard.java @@ -0,0 +1,69 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gpu; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +/** + * GPU card interface representing a physical GPU card model + */ +public interface GpuCard extends InternalIdentity, Identity { + /** + * @return the UUID of the GPU card + */ + String getUuid(); + + /** + * @return the device ID of the GPU card + */ + String getDeviceId(); + + /** + * @return the device name of the GPU card + */ + String getDeviceName(); + + /** + * @return the name of the GPU card + */ + String getName(); + + /** + * @return the vendor name of the GPU card + */ + String getVendorName(); + + /** + * @return the vendor ID of the GPU card + */ + String getVendorId(); + + /** + * @return the date when the GPU card was created + */ + Date getCreated(); + + + /** + * @return the group name of the GPU card based on how the XenServer expects it. + */ + String getGroupName(); + +} diff --git a/api/src/main/java/org/apache/cloudstack/gpu/GpuDevice.java b/api/src/main/java/org/apache/cloudstack/gpu/GpuDevice.java new file mode 100644 index 000000000000..22adda464779 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gpu/GpuDevice.java @@ -0,0 +1,42 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gpu; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +/** + * GPU device interface representing a physical GPU device + */ +public interface GpuDevice extends InternalIdentity, Identity { + + enum State { + Allocated, Free, Error, PartiallyAllocated, + } + + enum ManagedState { + Managed, Unmanaged, + } + + enum DeviceType { + PCI, MDEV, VGPUOnly, + } + + long getHostId(); + + State getState(); +} diff --git a/api/src/main/java/org/apache/cloudstack/gpu/GpuService.java b/api/src/main/java/org/apache/cloudstack/gpu/GpuService.java new file mode 100644 index 000000000000..1e7928a319f6 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gpu/GpuService.java @@ -0,0 +1,157 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gpu; + +import com.cloud.agent.api.VgpuTypesInfo; +import com.cloud.agent.api.to.GPUDeviceTO; +import com.cloud.host.Host; +import com.cloud.utils.component.Manager; +import com.cloud.vm.VirtualMachine; +import org.apache.cloudstack.api.command.admin.gpu.CreateGpuCardCmd; +import org.apache.cloudstack.api.command.admin.gpu.CreateGpuDeviceCmd; +import org.apache.cloudstack.api.command.admin.gpu.CreateVgpuProfileCmd; +import org.apache.cloudstack.api.command.admin.gpu.DeleteGpuCardCmd; +import org.apache.cloudstack.api.command.admin.gpu.DeleteGpuDeviceCmd; +import org.apache.cloudstack.api.command.admin.gpu.DeleteVgpuProfileCmd; +import org.apache.cloudstack.api.command.admin.gpu.UnmanageGpuDeviceCmd; +import org.apache.cloudstack.api.command.admin.gpu.DiscoverGpuDevicesCmd; +import org.apache.cloudstack.api.command.admin.gpu.ManageGpuDeviceCmd; +import org.apache.cloudstack.api.command.user.gpu.ListGpuDevicesCmd; +import org.apache.cloudstack.api.command.admin.gpu.UpdateGpuCardCmd; +import org.apache.cloudstack.api.command.admin.gpu.UpdateGpuDeviceCmd; +import org.apache.cloudstack.api.command.admin.gpu.UpdateVgpuProfileCmd; +import org.apache.cloudstack.api.command.user.gpu.ListGpuCardsCmd; +import org.apache.cloudstack.api.command.user.gpu.ListVgpuProfilesCmd; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.VgpuProfileResponse; +import org.apache.cloudstack.framework.config.ConfigKey; + +import java.util.HashMap; +import java.util.List; + +public interface GpuService extends Manager { + + ConfigKey GpuDetachOnStop = new ConfigKey<>(Boolean.class, "gpu.detach.on.stop", "Advanced", "false", + "Whether to detach GPU devices from VM on stop or keep them allocated", true, ConfigKey.Scope.Domain, null); + + GpuCard createGpuCard(CreateGpuCardCmd cmd); + + GpuCard updateGpuCard(UpdateGpuCardCmd cmd); + + boolean deleteGpuCard(DeleteGpuCardCmd cmd); + + VgpuProfileResponse createVgpuProfile(CreateVgpuProfileCmd cmd); + + VgpuProfileResponse updateVgpuProfile(UpdateVgpuProfileCmd cmd); + + boolean deleteVgpuProfile(DeleteVgpuProfileCmd cmd); + + ListResponse listGpuCards(ListGpuCardsCmd cmd); + + ListResponse listVgpuProfiles(ListVgpuProfilesCmd cmd); + + GpuDeviceResponse createGpuDevice(CreateGpuDeviceCmd cmd); + + GpuDeviceResponse updateGpuDevice(UpdateGpuDeviceCmd cmd); + + ListResponse listGpuDevices(ListGpuDevicesCmd cmd); + + boolean disableGpuDevice(UnmanageGpuDeviceCmd cmd); + + boolean enableGpuDevice(ManageGpuDeviceCmd cmd); + + /** + * Deallocate GPU devices for a VM on a host. + * + * @param vmId The ID of the VM to deallocate GPU devices for. + */ + void deallocateAllGpuDevicesForVm(long vmId); + + + /** + * Deallocate GPU devices for a VM on a host. + * + * @param vmId The ID of the VM to deallocate GPU devices for. + */ + void deallocateGpuDevicesForVmOnHost(long vmId, long hostId); + + /** + * Deallocate existing GPU devices for a VM on a host and allocate new GPU devices to the VM. + * + * @param vmId The ID of the VM to allocate GPU devices to. + * @param hostId The ID of the host to allocate GPU devices to. + * @param gpuDevices The list of GPU devices to allocate to the VM. + */ + void allocateGpuDevicesToVmOnHost(long vmId, long hostId, List gpuDevices); + + /** + * Discover GPU devices on a host by using the getGPUStatistics command and updating the GPU details for the host. + * + * @param cmd The command to discover GPU devices. + * @return The list of GPU devices. + */ + ListResponse discoverGpuDevices(DiscoverGpuDevicesCmd cmd); + + /** + * Check if GPU devices are available for a VM on a host by checking the number of available GPU devices for the + * vGPU profile. + * + * @param host The host to check GPU devices for. + * @param vmId The ID of the VM to check GPU devices for. + * @param vgpuProfile The vGPU profile to check GPU devices for. + * @param gpuCount The number of GPU devices to check for. + * @return True if GPU devices are available, false otherwise. + */ + boolean isGPUDeviceAvailable(Host host, Long vmId, VgpuProfile vgpuProfile, int gpuCount); + + /** + * Get GPU devices for a VM on a host by checking the number of available GPU devices for the vGPU profile. + * If the VM already has GPU devices assigned, deallocate them and allocate new GPU devices to the VM. + * The new GPU devices are allocated optimally to the VM. + * + * @param vm The VM to get GPU devices for. + * @param vgpuProfile The vGPU profile to get GPU devices for. + * @param gpuCount The number of GPU devices to get. + * @return The GPU devices. + */ + GPUDeviceTO getGPUDevice(VirtualMachine vm, long hostId, VgpuProfile vgpuProfile, int gpuCount); + + /** + * Gets the GPU group details from the GPU devices on a host. + * This fetches the GPU devices from the host and prepares the GPU group details for the host. + * The GPU group details are a map of GPU group name (Card's device name) to a map of vGPU profile name to + * VgpuTypesInfo. + * The VgpuTypesInfo contains the information about the GPU device. + * + * @param hostId The host ID to get GPU group details for. + * @return The GPU group details. + */ + HashMap> getGpuGroupDetailsFromGpuDevicesOnHost(long hostId); + + /** + * This method is used to add the GPU devices to the host when the host is discovered or when the GPU devices are + * updated. + * + * @param host The host to add the GPU devices to. + * @param newGpuDevicesInfo The list of GPU devices to add to the host. + */ + void addGpuDevicesToHost(Host host, List newGpuDevicesInfo); + + boolean deleteGpuDevices(DeleteGpuDeviceCmd deleteGpuDeviceCmd); +} diff --git a/api/src/main/java/org/apache/cloudstack/gpu/VgpuProfile.java b/api/src/main/java/org/apache/cloudstack/gpu/VgpuProfile.java new file mode 100644 index 000000000000..8cfac2a20de8 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gpu/VgpuProfile.java @@ -0,0 +1,74 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gpu; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +/** + * vGPU profile interface representing a virtualized GPU profile + */ +public interface VgpuProfile extends InternalIdentity, Identity { + /** + * @return the UUID of the vGPU profile + */ + String getUuid(); + + /** + * @return the name of the vGPU profile + */ + String getName(); + + /** + * @return the description of the vGPU profile + */ + String getDescription(); + + /** + * @return the date when the vGPU profile was created + */ + Date getCreated(); + + Long getCardId(); + + /** + * @return the maximum number of vGPUs per physical GPU + */ + Long getMaxVgpuPerPgpu(); + + /** + * @return the video RAM size in MB + */ + Long getVideoRam(); + + /** + * @return the maximum number of display heads + */ + Long getMaxHeads(); + + /** + * @return the maximum X resolution + */ + Long getMaxResolutionX(); + + /** + * @return the maximum Y resolution + */ + Long getMaxResolutionY(); +} diff --git a/api/src/main/java/org/apache/cloudstack/gui/theme/GuiTheme.java b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiTheme.java new file mode 100644 index 000000000000..0cca8bc2d7db --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiTheme.java @@ -0,0 +1,61 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gui.theme; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +public interface GuiTheme extends InternalIdentity, Identity { + + String getName(); + + String getDescription(); + + String getCss(); + + String getJsonConfiguration(); + + Date getCreated(); + + Date getRemoved(); + + boolean getIsPublic(); + + void setId(Long id); + + void setUuid(String uuid); + + void setName(String name); + + void setDescription(String description); + + void setCss(String css); + + void setJsonConfiguration(String jsonConfiguration); + + void setCreated(Date created); + + void setRemoved(Date removed); + + void setIsPublic(boolean isPublic); + + boolean isRecursiveDomains(); + + void setRecursiveDomains(boolean recursiveDomains); +} diff --git a/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeDetails.java b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeDetails.java new file mode 100644 index 000000000000..164535033d98 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeDetails.java @@ -0,0 +1,36 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gui.theme; + +import org.apache.cloudstack.api.InternalIdentity; + +public interface GuiThemeDetails extends InternalIdentity { + + void setId(Long id); + + Long getGuiThemeId(); + + void setGuiThemeId(Long guiThemeId); + + String getType(); + + void setType(String type); + + String getValue(); + + void setValue(String value); +} diff --git a/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoin.java b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoin.java new file mode 100644 index 000000000000..e54d53138ef6 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoin.java @@ -0,0 +1,47 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gui.theme; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +public interface GuiThemeJoin extends InternalIdentity, Identity { + + String getName(); + + String getDescription(); + + String getCss(); + + String getJsonConfiguration(); + + String getCommonNames(); + + String getDomains(); + + String getAccounts(); + + boolean isRecursiveDomains(); + + boolean getIsPublic(); + + Date getCreated(); + + Date getRemoved(); +} diff --git a/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeService.java b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeService.java new file mode 100644 index 000000000000..8a066d3ffeca --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeService.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.gui.theme; + +import org.apache.cloudstack.api.command.user.gui.theme.CreateGuiThemeCmd; +import org.apache.cloudstack.api.command.user.gui.theme.ListGuiThemesCmd; +import org.apache.cloudstack.api.command.user.gui.theme.RemoveGuiThemeCmd; +import org.apache.cloudstack.api.command.user.gui.theme.UpdateGuiThemeCmd; +import org.apache.cloudstack.api.response.GuiThemeResponse; +import org.apache.cloudstack.api.response.ListResponse; + +public interface GuiThemeService { + + ListResponse listGuiThemes(ListGuiThemesCmd cmd); + + GuiThemeJoin createGuiTheme(CreateGuiThemeCmd cmd); + + GuiThemeJoin updateGuiTheme(UpdateGuiThemeCmd cmd); + + void removeGuiTheme(RemoveGuiThemeCmd cmd); +} diff --git a/api/src/main/java/org/apache/cloudstack/management/ManagementServerHost.java b/api/src/main/java/org/apache/cloudstack/management/ManagementServerHost.java index 54a53f39578d..7f81523dab7d 100644 --- a/api/src/main/java/org/apache/cloudstack/management/ManagementServerHost.java +++ b/api/src/main/java/org/apache/cloudstack/management/ManagementServerHost.java @@ -22,7 +22,7 @@ public interface ManagementServerHost extends InternalIdentity, Identity, ControlledEntity { enum State { - Up, Down, PreparingToShutDown, ReadyToShutDown, ShuttingDown + Up, Down, PreparingForMaintenance, Maintenance, PreparingForShutDown, ReadyToShutDown, ShuttingDown } long getMsid(); diff --git a/api/src/main/java/org/apache/cloudstack/network/BgpPeer.java b/api/src/main/java/org/apache/cloudstack/network/BgpPeer.java new file mode 100644 index 000000000000..e1d7eca0a03e --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/network/BgpPeer.java @@ -0,0 +1,50 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.network; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +public interface BgpPeer extends Identity, InternalIdentity { + + Long getDomainId(); + + Long getAccountId(); + + enum State { + Active, Add, Revoke + } + + enum Detail { + EBGP_MultiHop + } + + long getDataCenterId(); + + String getIp4Address(); + + String getIp6Address(); + + Long getAsNumber(); + + String getPassword(); + + Date getCreated(); +} diff --git a/api/src/main/java/org/apache/cloudstack/network/BgpPeerTO.java b/api/src/main/java/org/apache/cloudstack/network/BgpPeerTO.java new file mode 100644 index 000000000000..b05033146169 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/network/BgpPeerTO.java @@ -0,0 +1,91 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.network; + +import java.util.Map; + +public class BgpPeerTO { + Long peerId; + Long peerAsNumber; + String ip4Address; + String ip6Address; + String peerPassword; + Long networkId; + Long networkAsNumber; + String guestIp4Cidr; + String guestIp6Cidr; + + Map details; + + public BgpPeerTO(Long peerId, String ip4Address, String ip6Address, Long peerAsNumber, String peerPassword, + Long networkId, Long networkAsNumber, String guestIp4Cidr, String guestIp6Cidr, Map details) { + this.peerId = peerId; + this.ip4Address = ip4Address; + this.ip6Address = ip6Address; + this.peerAsNumber = peerAsNumber; + this.peerPassword = peerPassword; + this.networkId = networkId; + this.networkAsNumber = networkAsNumber; + this.guestIp4Cidr = guestIp4Cidr; + this.guestIp6Cidr = guestIp6Cidr; + this.details = details; + } + + public BgpPeerTO(Long networkId) { + this.networkId = networkId; + } + + public Long getPeerId() { + return peerId; + } + + public String getIp4Address() { + return ip4Address; + } + + public String getIp6Address() { + return ip6Address; + } + + public Long getPeerAsNumber() { + return peerAsNumber; + } + + public String getPeerPassword() { + return peerPassword; + } + + public Long getNetworkId() { + return networkId; + } + + public Long getNetworkAsNumber() { + return networkAsNumber; + } + + public String getGuestIp4Cidr() { + return guestIp4Cidr; + } + + public String getGuestIp6Cidr() { + return guestIp6Cidr; + } + + public Map getDetails() { + return details; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/network/Ipv4GuestSubnetNetworkMap.java b/api/src/main/java/org/apache/cloudstack/network/Ipv4GuestSubnetNetworkMap.java new file mode 100644 index 000000000000..569ed22c1640 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/network/Ipv4GuestSubnetNetworkMap.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.network; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +public interface Ipv4GuestSubnetNetworkMap extends Identity, InternalIdentity { + Date getAllocated(); + + Date getCreated(); + + enum State { + Allocating, // The subnet will be assigned to a network + Allocated, // The subnet is in use. + Releasing, // The subnet is being released. + Free // The subnet is ready to be allocated. + } + + Long getParentId(); + + String getSubnet(); + + Long getVpcId(); + + Long getNetworkId(); + + State getState(); + +} diff --git a/api/src/main/java/org/apache/cloudstack/network/RoutedIpv4Manager.java b/api/src/main/java/org/apache/cloudstack/network/RoutedIpv4Manager.java new file mode 100644 index 000000000000..9285331f41aa --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/network/RoutedIpv4Manager.java @@ -0,0 +1,208 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.network; + +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.network.Network; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.vpc.Vpc; +import com.cloud.network.vpc.VpcOffering; +import com.cloud.offering.NetworkOffering; +import com.cloud.user.Account; +import com.cloud.utils.Pair; +import com.cloud.utils.component.PluggableService; + +import org.apache.cloudstack.api.command.admin.network.CreateIpv4SubnetForZoneCmd; +import org.apache.cloudstack.api.command.admin.network.CreateIpv4SubnetForGuestNetworkCmd; +import org.apache.cloudstack.api.command.admin.network.DedicateIpv4SubnetForZoneCmd; +import org.apache.cloudstack.api.command.admin.network.DeleteIpv4SubnetForZoneCmd; +import org.apache.cloudstack.api.command.admin.network.DeleteIpv4SubnetForGuestNetworkCmd; +import org.apache.cloudstack.api.command.admin.network.ListIpv4SubnetsForZoneCmd; +import org.apache.cloudstack.api.command.admin.network.ListIpv4SubnetsForGuestNetworkCmd; +import org.apache.cloudstack.api.command.admin.network.ReleaseDedicatedIpv4SubnetForZoneCmd; +import org.apache.cloudstack.api.command.admin.network.UpdateIpv4SubnetForZoneCmd; +import org.apache.cloudstack.api.command.admin.network.bgp.ChangeBgpPeersForNetworkCmd; +import org.apache.cloudstack.api.command.admin.network.bgp.ChangeBgpPeersForVpcCmd; +import org.apache.cloudstack.api.command.admin.network.bgp.CreateBgpPeerCmd; +import org.apache.cloudstack.api.command.admin.network.bgp.DedicateBgpPeerCmd; +import org.apache.cloudstack.api.command.admin.network.bgp.DeleteBgpPeerCmd; +import org.apache.cloudstack.api.command.admin.network.bgp.ListBgpPeersCmd; +import org.apache.cloudstack.api.command.admin.network.bgp.ReleaseDedicatedBgpPeerCmd; +import org.apache.cloudstack.api.command.admin.network.bgp.UpdateBgpPeerCmd; +import org.apache.cloudstack.api.command.user.network.routing.CreateRoutingFirewallRuleCmd; +import org.apache.cloudstack.api.command.user.network.routing.ListRoutingFirewallRulesCmd; +import org.apache.cloudstack.api.command.user.network.routing.UpdateRoutingFirewallRuleCmd; +import org.apache.cloudstack.api.response.BgpPeerResponse; +import org.apache.cloudstack.api.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.api.response.Ipv4SubnetForGuestNetworkResponse; +import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; + +import java.util.List; + +public interface RoutedIpv4Manager extends PluggableService, Configurable { + + ConfigKey RoutedNetworkVpcEnabled = new ConfigKey<>(ConfigKey.CATEGORY_NETWORK, Boolean.class, + "routed.network.vpc.enabled", + "true", + "If true, the Routed network and VPC are enabled in the zone.", + true, + ConfigKey.Scope.Zone); + + ConfigKey RoutedNetworkIPv4MaxCidrSize = new ConfigKey<>(ConfigKey.CATEGORY_NETWORK, Integer.class, + "routed.network.ipv4.max.cidr.size", "30", "The maximum value of the cidr size for isolated networks in ROUTED mode", + true, ConfigKey.Scope.Account); + + ConfigKey RoutedNetworkIPv4MinCidrSize = new ConfigKey<>(ConfigKey.CATEGORY_NETWORK, Integer.class, + "routed.network.ipv4.min.cidr.size", "24", "The minimum value of the cidr size for isolated networks in ROUTED mode", + true, ConfigKey.Scope.Account); + + ConfigKey RoutedVpcIPv4MaxCidrSize = new ConfigKey<>(ConfigKey.CATEGORY_NETWORK, Integer.class, + "routed.ipv4.vpc.max.cidr.size", "28", "The maximum value of the cidr size for VPC in ROUTED mode", + true, ConfigKey.Scope.Account); + + ConfigKey RoutedVpcIPv4MinCidrSize = new ConfigKey<>(ConfigKey.CATEGORY_NETWORK, Integer.class, + "routed.ipv4.vpc.min.cidr.size", "22", "The minimum value of the cidr size for VPC in ROUTED mode", + true, ConfigKey.Scope.Account); + + ConfigKey RoutedIPv4NetworkCidrAutoAllocationEnabled = new ConfigKey<>(ConfigKey.CATEGORY_NETWORK, Boolean.class, + "routed.ipv4.network.cidr.auto.allocation.enabled", + "true", + "Indicates whether the auto-allocation of network CIDR for routed network is enabled or not.", + true, + ConfigKey.Scope.Account); + + ConfigKey UseSystemBgpPeers = new ConfigKey<>(ConfigKey.CATEGORY_NETWORK, Boolean.class, + "use.system.bgp.peers", + "true", + "If true, when account has dedicated bgp peers(s), the guest networks with dynamic routing will use both system and dedicated bgp peers. If false, only dedicated bgp peers will be used.", + true, + ConfigKey.Scope.Account); + + // Methods for DataCenterIpv4GuestSubnet APIs + DataCenterIpv4GuestSubnet createDataCenterIpv4GuestSubnet(CreateIpv4SubnetForZoneCmd createIpv4SubnetForZoneCmd); + + DataCenterIpv4SubnetResponse createDataCenterIpv4SubnetResponse(DataCenterIpv4GuestSubnet result); + + boolean deleteDataCenterIpv4GuestSubnet(DeleteIpv4SubnetForZoneCmd deleteIpv4SubnetForZoneCmd); + + DataCenterIpv4GuestSubnet updateDataCenterIpv4GuestSubnet(UpdateIpv4SubnetForZoneCmd updateIpv4SubnetForZoneCmd); + + List listDataCenterIpv4GuestSubnets(ListIpv4SubnetsForZoneCmd listIpv4SubnetsForZoneCmd); + + DataCenterIpv4GuestSubnet dedicateDataCenterIpv4GuestSubnet(DedicateIpv4SubnetForZoneCmd dedicateIpv4SubnetForZoneCmd); + + DataCenterIpv4GuestSubnet releaseDedicatedDataCenterIpv4GuestSubnet(ReleaseDedicatedIpv4SubnetForZoneCmd releaseDedicatedIpv4SubnetForZoneCmd); + + // Methods for Ipv4SubnetForGuestNetwork APIs + Ipv4GuestSubnetNetworkMap createIpv4SubnetForGuestNetwork(CreateIpv4SubnetForGuestNetworkCmd createIpv4SubnetForGuestNetworkCmd); + + boolean deleteIpv4SubnetForGuestNetwork(DeleteIpv4SubnetForGuestNetworkCmd deleteIpv4SubnetForGuestNetworkCmd); + + void releaseIpv4SubnetForGuestNetwork(long networkId); + + void releaseIpv4SubnetForVpc(long vpcId); + + List listIpv4GuestSubnetsForGuestNetwork(ListIpv4SubnetsForGuestNetworkCmd listIpv4SubnetsForGuestNetworkCmd); + + Ipv4SubnetForGuestNetworkResponse createIpv4SubnetForGuestNetworkResponse(Ipv4GuestSubnetNetworkMap subnet); + + // Methods for internal calls + void getOrCreateIpv4SubnetForGuestNetwork(Network network, String networkCidr); + + Ipv4GuestSubnetNetworkMap getOrCreateIpv4SubnetForGuestNetwork(Long domainId, Long accountId, Long zoneId, Integer networkCidrSize); + + void getOrCreateIpv4SubnetForVpc(Vpc vpc, String networkCidr); + + Ipv4GuestSubnetNetworkMap getOrCreateIpv4SubnetForVpc(Vpc vpc, Integer vpcCidrSize); + + void assignIpv4SubnetToNetwork(Network network); + + void assignIpv4SubnetToVpc(Vpc vpc); + + // Methods for Routing firewall rules + FirewallRule createRoutingFirewallRule(CreateRoutingFirewallRuleCmd createRoutingFirewallRuleCmd) throws NetworkRuleConflictException; + + Pair, Integer> listRoutingFirewallRules(ListRoutingFirewallRulesCmd listRoutingFirewallRulesCmd); + + FirewallRule updateRoutingFirewallRule(UpdateRoutingFirewallRuleCmd updateRoutingFirewallRuleCmd); + + boolean revokeRoutingFirewallRule(Long id); + + boolean applyRoutingFirewallRule(long id); + + boolean isVirtualRouterGateway(Network network); + + boolean isVirtualRouterGateway(NetworkOffering networkOffering); + + boolean isRoutedNetwork(Network network); + + boolean isDynamicRoutedNetwork(Network network); + + boolean isDynamicRoutedNetwork(NetworkOffering networkOffering); + + boolean isRoutedVpc(Vpc vpc); + + boolean isValidGateway(VpcOffering vpcOffering); + + BgpPeer createBgpPeer(CreateBgpPeerCmd createBgpPeerCmd); + + BgpPeerResponse createBgpPeerResponse(BgpPeer result); + + boolean deleteBgpPeer(DeleteBgpPeerCmd deleteBgpPeerCmd); + + BgpPeer updateBgpPeer(UpdateBgpPeerCmd updateBgpPeerCmd); + + BgpPeer dedicateBgpPeer(DedicateBgpPeerCmd dedicateBgpPeerCmd); + + BgpPeer releaseDedicatedBgpPeer(ReleaseDedicatedBgpPeerCmd releaseDedicatedBgpPeerCmd); + + List listBgpPeers(ListBgpPeersCmd listBgpPeersCmd); + + Network changeBgpPeersForNetwork(ChangeBgpPeersForNetworkCmd changeBgpPeersForNetworkCmd); + + Network removeBgpPeersFromNetwork(Network network); + + void validateBgpPeers(Account owner, Long zoneId, List bgpPeerIds); + + void persistBgpPeersForGuestNetwork(long networkId, List bgpPeerIds); + + void releaseBgpPeersForGuestNetwork(long networkId); + + boolean isDynamicRoutedVpc(Vpc vpc); + + boolean isDynamicRoutedVpc(VpcOffering vpcOff); + + void persistBgpPeersForVpc(long vpcId, List bgpPeerIds); + + void releaseBgpPeersForVpc(long vpcId); + + Vpc changeBgpPeersForVpc(ChangeBgpPeersForVpcCmd changeBgpPeersForVpcCmd); + + List getBgpPeerIdsForAccount(Account owner, long zoneIdd); + + void removeIpv4SubnetsForZoneByAccountId(long accountId); + + void removeIpv4SubnetsForZoneByDomainId(long domainId); + + void removeBgpPeersByAccountId(long accountId); + + void removeBgpPeersByDomainId(long domainId); + + Boolean isRoutedNetworkVpcEnabled(long zoneId); +} diff --git a/api/src/main/java/org/apache/cloudstack/network/element/InternalLoadBalancerElementService.java b/api/src/main/java/org/apache/cloudstack/network/element/InternalLoadBalancerElementService.java index 76706a4cfc9e..1fff54f5f8ff 100644 --- a/api/src/main/java/org/apache/cloudstack/network/element/InternalLoadBalancerElementService.java +++ b/api/src/main/java/org/apache/cloudstack/network/element/InternalLoadBalancerElementService.java @@ -52,4 +52,6 @@ public interface InternalLoadBalancerElementService extends PluggableService { * @return */ List searchForInternalLoadBalancerElements(Long id, Long ntwkSvsProviderId, Boolean enabled); + + VirtualRouterProvider.Type getProviderType(); } diff --git a/api/src/main/java/org/apache/cloudstack/outofbandmanagement/OutOfBandManagementService.java b/api/src/main/java/org/apache/cloudstack/outofbandmanagement/OutOfBandManagementService.java index d670e4d3a88f..4f6f1ad66c93 100644 --- a/api/src/main/java/org/apache/cloudstack/outofbandmanagement/OutOfBandManagementService.java +++ b/api/src/main/java/org/apache/cloudstack/outofbandmanagement/OutOfBandManagementService.java @@ -39,7 +39,7 @@ public interface OutOfBandManagementService { long getId(); boolean isOutOfBandManagementEnabled(Host host); void submitBackgroundPowerSyncTask(Host host); - boolean transitionPowerStateToDisabled(List hosts); + boolean transitionPowerStateToDisabled(List hostIds); OutOfBandManagementResponse enableOutOfBandManagement(DataCenter zone); OutOfBandManagementResponse enableOutOfBandManagement(Cluster cluster); 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 3299e7537a2e..5b053aafd84b 100644 --- a/api/src/main/java/org/apache/cloudstack/query/QueryService.java +++ b/api/src/main/java/org/apache/cloudstack/query/QueryService.java @@ -16,9 +16,11 @@ // under the License. package org.apache.cloudstack.query; +import java.util.Arrays; import java.util.List; import org.apache.cloudstack.affinity.AffinityGroupResponse; +import org.apache.cloudstack.api.ResponseObject; import org.apache.cloudstack.api.command.admin.domain.ListDomainsCmd; import org.apache.cloudstack.api.command.admin.host.ListHostTagsCmd; import org.apache.cloudstack.api.command.admin.host.ListHostsCmd; @@ -30,6 +32,7 @@ import org.apache.cloudstack.api.command.admin.storage.ListImageStoresCmd; import org.apache.cloudstack.api.command.admin.storage.ListObjectStoragePoolsCmd; import org.apache.cloudstack.api.command.admin.storage.ListSecondaryStagingStoresCmd; +import org.apache.cloudstack.api.command.admin.storage.ListStorageAccessGroupsCmd; import org.apache.cloudstack.api.command.admin.storage.ListStoragePoolsCmd; import org.apache.cloudstack.api.command.admin.storage.ListStorageTagsCmd; import org.apache.cloudstack.api.command.admin.storage.heuristics.ListSecondaryStorageSelectorsCmd; @@ -52,6 +55,7 @@ import org.apache.cloudstack.api.command.user.snapshot.ListSnapshotsCmd; import org.apache.cloudstack.api.command.user.tag.ListTagsCmd; import org.apache.cloudstack.api.command.user.template.ListTemplatesCmd; +import org.apache.cloudstack.api.command.admin.vm.ListAffectedVmsForStorageScopeChangeCmd; import org.apache.cloudstack.api.command.user.vm.ListVMsCmd; import org.apache.cloudstack.api.command.user.vmgroup.ListVMGroupsCmd; import org.apache.cloudstack.api.command.user.volume.ListResourceDetailsCmd; @@ -84,16 +88,19 @@ import org.apache.cloudstack.api.response.SecurityGroupResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; import org.apache.cloudstack.api.response.SnapshotResponse; +import org.apache.cloudstack.api.response.StorageAccessGroupResponse; import org.apache.cloudstack.api.response.StoragePoolResponse; import org.apache.cloudstack.api.response.StorageTagResponse; import org.apache.cloudstack.api.response.TemplateResponse; import org.apache.cloudstack.api.response.UserResponse; import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.api.response.VirtualMachineResponse; import org.apache.cloudstack.api.response.VolumeResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.framework.config.ConfigKey; import com.cloud.exception.PermissionDeniedException; +import com.cloud.vm.VmDetailConstants; /** * Service used for list api query. @@ -101,34 +108,45 @@ */ public interface QueryService { + List RootAdminOnlyVmSettings = Arrays.asList(VmDetailConstants.GUEST_CPU_MODE, VmDetailConstants.GUEST_CPU_MODEL); + // Config keys ConfigKey AllowUserViewDestroyedVM = new ConfigKey<>("Advanced", Boolean.class, "allow.user.view.destroyed.vm", "false", "Determines whether users can view their destroyed or expunging vm ", true, ConfigKey.Scope.Account); - static final ConfigKey UserVMDeniedDetails = new ConfigKey<>(String.class, + ConfigKey UserVMDeniedDetails = new ConfigKey<>(String.class, "user.vm.denied.details", "Advanced", "rootdisksize, cpuOvercommitRatio, memoryOvercommitRatio, Message.ReservedCapacityFreed.Flag", "Determines whether users can view certain VM settings. When set to empty, default value used is: rootdisksize, cpuOvercommitRatio, memoryOvercommitRatio, Message.ReservedCapacityFreed.Flag.", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.CSV, null); - static final ConfigKey UserVMReadOnlyDetails = new ConfigKey<>(String.class, + ConfigKey UserVMReadOnlyDetails = new ConfigKey<>(String.class, "user.vm.readonly.details", "Advanced", "dataDiskController, rootDiskController", - "List of read-only VM settings/details as comma separated string", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.CSV, null); + "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, " + "network offering, zones), we use the flag to determine if the entities should be sorted ascending (when flag is true) " + "or descending (when flag is false). Within the scope of the config all users see the same result.", true, ConfigKey.Scope.Global); - public static final ConfigKey AllowUserViewAllDomainAccounts = new ConfigKey<>("Advanced", Boolean.class, + ConfigKey AllowUserViewAllDomainAccounts = new ConfigKey<>("Advanced", Boolean.class, "allow.user.view.all.domain.accounts", "false", "Determines whether users can view all user accounts within the same domain", true, ConfigKey.Scope.Domain); - static final ConfigKey SharePublicTemplatesWithOtherDomains = new ConfigKey<>("Advanced", Boolean.class, "share.public.templates.with.other.domains", "true", - "If false, templates of this domain will not show up in the list templates of other domains.", 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); + + ConfigKey SharePublicTemplatesWithOtherDomains = new ConfigKey<>("Advanced", Boolean.class, "share.public.templates.with.other.domains", "true", + "If false, Templates of this domain will not show up in the list Templates of other domains.", true, ConfigKey.Scope.Domain); + + ConfigKey ReturnVmStatsOnVmList = new ConfigKey<>("Advanced", Boolean.class, "list.vm.default.details.stats", "true", + "Determines whether VM stats should be returned when details are not explicitly specified in listVirtualMachines API request. When false, details default to [group, nics, secgrp, tmpl, servoff, diskoff, backoff, iso, volume, min, affgrp]. When true, all details are returned including 'stats'.", true, ConfigKey.Scope.Global); + - ListResponse searchForUsers(ListUsersCmd cmd) throws PermissionDeniedException; + ListResponse searchForUsers(ResponseObject.ResponseView responseView, ListUsersCmd cmd) throws PermissionDeniedException; ListResponse searchForUsers(Long domainId, boolean recursive) throws PermissionDeniedException; + List searchForAccessibleUsers(); + ListResponse searchForEvents(ListEventsCmd cmd); ListResponse listTags(ListTagsCmd cmd); @@ -137,6 +155,8 @@ public interface QueryService { ListResponse searchForUserVMs(ListVMsCmd cmd); + ListResponse listAffectedVmsForStorageScopeChange(ListAffectedVmsForStorageScopeChangeCmd cmd); + ListResponse searchForSecurityGroups(ListSecurityGroupsCmd cmd); ListResponse searchForRouters(ListRoutersCmd cmd); @@ -185,6 +205,8 @@ public interface QueryService { ListResponse searchForStorageTags(ListStorageTagsCmd cmd); + ListResponse searchForStorageAccessGroups(ListStorageAccessGroupsCmd cmd); + ListResponse searchForHostTags(ListHostTagsCmd cmd); ListResponse listManagementServers(ListMgmtsCmd cmd); diff --git a/api/src/main/java/org/apache/cloudstack/quota/QuotaTariff.java b/api/src/main/java/org/apache/cloudstack/quota/QuotaTariff.java new file mode 100644 index 000000000000..7de66a168f98 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/quota/QuotaTariff.java @@ -0,0 +1,25 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.quota; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +public interface QuotaTariff extends InternalIdentity, Identity { + +} diff --git a/api/src/main/java/org/apache/cloudstack/resource/ResourceCleanupService.java b/api/src/main/java/org/apache/cloudstack/resource/ResourceCleanupService.java new file mode 100644 index 000000000000..0d72edb07489 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/resource/ResourceCleanupService.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.resource; + +import org.apache.cloudstack.api.command.admin.resource.PurgeExpungedResourcesCmd; +import org.apache.cloudstack.framework.config.ConfigKey; + +import com.cloud.vm.VirtualMachine; + +public interface ResourceCleanupService { + int MINIMUM_EXPUNGED_RESOURCE_PURGE_JOB_DELAY_IN_SECONDS = 3 * 60; + ConfigKey ExpungedResourcePurgeEnabled = new ConfigKey<>("Advanced", Boolean.class, + "expunged.resources.purge.enabled", "false", + "Whether to run a background task to purge the DB records of the expunged resources", + false, ConfigKey.Scope.Global); + ConfigKey ExpungedResourcePurgeResources = new ConfigKey<>("Advanced", String.class, + "expunged.resources.purge.resources", "", + "A comma-separated list of resource types that will be considered by the background task " + + "to purge the DB records of the expunged resources. Currently only VirtualMachine is supported. " + + "An empty value will result in considering all resource types for purging", + false, ConfigKey.Scope.Global); + ConfigKey ExpungedResourcesPurgeInterval = new ConfigKey<>("Advanced", Integer.class, + "expunged.resources.purge.interval", "86400", + "Interval (in seconds) for the background task to purge the DB records of the expunged resources", + false); + ConfigKey ExpungedResourcesPurgeDelay = new ConfigKey<>("Advanced", Integer.class, + "expunged.resources.purge.delay", "300", + "Initial delay (in seconds) to start the background task to purge the DB records of the " + + "expunged resources task", false); + ConfigKey ExpungedResourcesPurgeBatchSize = new ConfigKey<>("Advanced", Integer.class, + "expunged.resources.purge.batch.size", "50", + "Batch size to be used during purging of the DB records of the expunged resources", + true); + ConfigKey ExpungedResourcesPurgeStartTime = new ConfigKey<>("Advanced", String.class, + "expunged.resources.purge.start.time", "", + "Start time to be used by the background task to purge the DB records of the expunged " + + "resources. Use format \"yyyy-MM-dd\" or \"yyyy-MM-dd HH:mm:ss\"", true); + ConfigKey ExpungedResourcesPurgeKeepPastDays = new ConfigKey<>("Advanced", Integer.class, + "expunged.resources.purge.keep.past.days", "30", + "The number of days in the past from the execution time of the background task to purge " + + "the DB records of the expunged resources for which the expunged resources must not be purged. " + + "To enable purging DB records of the expunged resource till the execution of the background " + + "task, set the value to zero.", true); + ConfigKey ExpungedResourcePurgeJobDelay = new ConfigKey<>("Advanced", Integer.class, + "expunged.resource.purge.job.delay", + String.valueOf(MINIMUM_EXPUNGED_RESOURCE_PURGE_JOB_DELAY_IN_SECONDS), + String.format("Delay (in seconds) to execute the purging of the DB records of an expunged resource " + + "initiated by the configuration in the offering. Minimum value should be %d seconds " + + "and if a lower value is set then the minimum value will be used", + MINIMUM_EXPUNGED_RESOURCE_PURGE_JOB_DELAY_IN_SECONDS), + true); + + enum ResourceType { + VirtualMachine + } + + long purgeExpungedResources(PurgeExpungedResourcesCmd cmd); + void purgeExpungedVmResourcesLaterIfNeeded(VirtualMachine vm); +} diff --git a/api/src/main/java/org/apache/cloudstack/storage/browser/DataStoreObjectResponse.java b/api/src/main/java/org/apache/cloudstack/storage/browser/DataStoreObjectResponse.java index cac5cc91b03e..c281fa115fdd 100644 --- a/api/src/main/java/org/apache/cloudstack/storage/browser/DataStoreObjectResponse.java +++ b/api/src/main/java/org/apache/cloudstack/storage/browser/DataStoreObjectResponse.java @@ -41,6 +41,10 @@ public class DataStoreObjectResponse extends BaseResponse { @Param(description = "Template ID associated with the data store object.") private String templateId; + @SerializedName(ApiConstants.TEMPLATE_NAME) + @Param(description = "Template Name associated with the data store object.") + private String templateName; + @SerializedName(ApiConstants.FORMAT) @Param(description = "Format of template associated with the data store object.") private String format; @@ -49,10 +53,18 @@ public class DataStoreObjectResponse extends BaseResponse { @Param(description = "Snapshot ID associated with the data store object.") private String snapshotId; + @SerializedName("snapshotname") + @Param(description = "Snapshot Name associated with the data store object.") + private String snapshotName; + @SerializedName(ApiConstants.VOLUME_ID) @Param(description = "Volume ID associated with the data store object.") private String volumeId; + @SerializedName(ApiConstants.VOLUME_NAME) + @Param(description = "Volume Name associated with the data store object.") + private String volumeName; + @SerializedName(ApiConstants.LAST_UPDATED) @Param(description = "Last modified date of the file/directory.") private Date lastUpdated; @@ -86,6 +98,18 @@ public void setVolumeId(String volumeId) { this.volumeId = volumeId; } + public void setTemplateName(String templateName) { + this.templateName = templateName; + } + + public void setSnapshotName(String snapshotName) { + this.snapshotName = snapshotName; + } + + public void setVolumeName(String volumeName) { + this.volumeName = volumeName; + } + public String getName() { return name; } @@ -117,4 +141,16 @@ public String getVolumeId() { public Date getLastUpdated() { return lastUpdated; } + + public String getTemplateName() { + return templateName; + } + + public String getSnapshotName() { + return snapshotName; + } + + public String getVolumeName() { + return volumeName; + } } 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 7e1361d1e71f..e27ef308d7f2 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 @@ -22,10 +22,59 @@ import com.cloud.user.Account; import org.apache.cloudstack.api.command.user.bucket.CreateBucketCmd; import org.apache.cloudstack.api.command.user.bucket.UpdateBucketCmd; +import org.apache.cloudstack.framework.config.ConfigKey; public interface BucketApiService { + ConfigKey DefaultMaxAccountBuckets = new ConfigKey("Account Defaults", Long.class, + "max.account.buckets", + "20", + "The default maximum number of buckets that can be created for an account", + false, + ConfigKey.Scope.Global, + null); + + ConfigKey DefaultMaxAccountObjectStorage = new ConfigKey("Account Defaults", Long.class, + "max.account.object.storage", + "400", + "The default maximum object storage space (in GiB) that can be used for an account", + false, + ConfigKey.Scope.Global, + null); + + ConfigKey DefaultMaxProjectBuckets = new ConfigKey("Project Defaults", Long.class, + "max.project.buckets", + "20", + "The default maximum number of buckets that can be created for a project", + false, + ConfigKey.Scope.Global, + null); + + ConfigKey DefaultMaxProjectObjectStorage = new ConfigKey("Project Defaults", Long.class, + "max.project.object.storage", + "400", + "The default maximum object storage space (in GiB) that can be used for a project", + false, + ConfigKey.Scope.Global, + null); + + ConfigKey DefaultMaxDomainBuckets = new ConfigKey("Domain Defaults", Long.class, + "max.domain.buckets", + "20", + "The default maximum number of buckets that can be created for a domain", + false, + ConfigKey.Scope.Global, + null); + + ConfigKey DefaultMaxDomainObjectStorage = new ConfigKey("Domain Defaults", Long.class, + "max.domain.object.storage", + "400", + "The default maximum object storage space (in GiB) that can be used for a domain", + false, + ConfigKey.Scope.Global, + null); + /** * Creates the database object for a Bucket based on the given criteria * @@ -48,7 +97,7 @@ public interface BucketApiService { boolean deleteBucket(long bucketId, Account caller); - boolean updateBucket(UpdateBucketCmd cmd, Account caller); + boolean updateBucket(UpdateBucketCmd cmd, Account caller) throws ResourceAllocationException; void getBucketUsage(); } diff --git a/api/src/main/java/org/apache/cloudstack/storage/sharedfs/SharedFS.java b/api/src/main/java/org/apache/cloudstack/storage/sharedfs/SharedFS.java new file mode 100644 index 000000000000..bcba425abbff --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/storage/sharedfs/SharedFS.java @@ -0,0 +1,189 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.storage.sharedfs; + +import com.cloud.utils.fsm.StateMachine2; +import com.cloud.utils.fsm.StateObject; + +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; +import org.apache.cloudstack.framework.config.ConfigKey; + +import java.util.Date; + +public interface SharedFS extends ControlledEntity, Identity, InternalIdentity, StateObject { + + static final ConfigKey SharedFSFeatureEnabled = new ConfigKey("Advanced", Boolean.class, + "sharedfs.feature.enabled", + "true", + " Indicates whether the Shared FileSystem feature is enabled or not. Management server restart needed on change", + false); + + ConfigKey SharedFSCleanupInterval = new ConfigKey<>(Integer.class, + "sharedfs.cleanup.interval", + "Advanced", + "14400", + "The interval (in seconds) to wait before running the shared filesystem cleanup thread.", + false, + ConfigKey.Scope.Global, + null, + SharedFSFeatureEnabled.key()); + + ConfigKey SharedFSCleanupDelay = new ConfigKey<>(Integer.class, + "sharedfs.cleanup.delay", + "Advanced", + "86400", + "Determines how long (in seconds) to wait before actually expunging destroyed shared filesystem.", + false, + ConfigKey.Scope.Global, + null, + SharedFSFeatureEnabled.key()); + + ConfigKey SharedFSExpungeWorkers = new ConfigKey<>(Integer.class, + "sharedfs.expunge.workers", + "Advanced", + "2", + "Determines how many threads are created to do the work of expunging destroyed shared filesystem.", + false, + ConfigKey.Scope.Global, + null, + SharedFSFeatureEnabled.key()); + + String SharedFSVmNamePrefix = "sharedfs"; + String SharedFSPath = "/export"; + + enum FileSystemType { + EXT4, XFS + } + + enum Protocol { + NFS + } + + enum State { + Allocated(false, "The shared filesystem is allocated in db but hasn't been created or started yet."), + Ready(false, "The shared filesystem is ready to use."), + Stopping(true, "The shared filesystem is being stopped"), + Stopped(false, "The shared filesystem is in stopped state. It can not be used but the data is still there."), + Starting(true, "The shared filesystem is being started."), + Destroyed(false, "The shared filesystem is destroyed."), + Expunging(true, "The shared filesystem is being expunged."), + Expunged(false, "The shared filesystem has been expunged."), + Error(false, "The shared filesystem is in error state."); + + boolean _transitional; + String _description; + + /** + * SharedFS State + * + * @param transitional true for transition/non-final state, otherwise false + * @param description description of the state + */ + State(boolean transitional, String description) { + _transitional = transitional; + _description = description; + } + + public boolean isTransitional() { + return _transitional; + } + + public String getDescription() { + return _description; + } + + private final static StateMachine2 s_fsm = new StateMachine2(); + + public static StateMachine2 getStateMachine() { + return s_fsm; + } + + static { + s_fsm.addTransition(new StateMachine2.Transition(Allocated, Event.OperationFailed, Error, null)); + s_fsm.addTransition(new StateMachine2.Transition(Allocated, Event.OperationSucceeded, Ready, null)); + s_fsm.addTransition(new StateMachine2.Transition(Error, Event.DestroyRequested, Destroyed, null)); + s_fsm.addTransition(new StateMachine2.Transition(Stopped, Event.StartRequested, Starting, null)); + s_fsm.addTransition(new StateMachine2.Transition(Starting, Event.OperationSucceeded, Ready, null)); + s_fsm.addTransition(new StateMachine2.Transition(Starting, Event.OperationFailed, Stopped, null)); + s_fsm.addTransition(new StateMachine2.Transition(Ready, Event.StopRequested, Stopping, null)); + s_fsm.addTransition(new StateMachine2.Transition(Stopping, Event.OperationSucceeded, Stopped, null)); + s_fsm.addTransition(new StateMachine2.Transition(Stopping, Event.OperationFailed, Ready, null)); + s_fsm.addTransition(new StateMachine2.Transition(Stopped, Event.DestroyRequested, Destroyed, null)); + s_fsm.addTransition(new StateMachine2.Transition(Destroyed, Event.RecoveryRequested, Stopped, null)); + s_fsm.addTransition(new StateMachine2.Transition(Destroyed, Event.ExpungeOperation, Expunging, null)); + s_fsm.addTransition(new StateMachine2.Transition(Error, Event.ExpungeOperation, Expunging, null)); + s_fsm.addTransition(new StateMachine2.Transition(Expunging, Event.ExpungeOperation, Expunging, null)); + s_fsm.addTransition(new StateMachine2.Transition(Expunging, Event.OperationSucceeded, Expunged, null)); + } + } + + enum Event { + StopRequested, + StartRequested, + DestroyRequested, + OperationSucceeded, + OperationFailed, + ExpungeOperation, + RecoveryRequested, + } + + static String getSharedFSPath() { + return SharedFSPath; + } + + long getId(); + + String getName(); + + void setName(String name); + + String getUuid(); + + String getDescription(); + + void setDescription(String description); + + Long getDataCenterId(); + + State getState(); + + String getFsProviderName(); + + Protocol getProtocol(); + + Long getVolumeId(); + + void setVolumeId(Long volumeId); + + Long getVmId(); + + void setVmId(Long vmId); + + FileSystemType getFsType(); + + Long getServiceOfferingId(); + + void setServiceOfferingId(Long serviceOfferingId); + + Date getUpdated(); + + public long getUpdatedCount(); + + public void incrUpdatedCount(); +} diff --git a/api/src/main/java/org/apache/cloudstack/storage/sharedfs/SharedFSLifeCycle.java b/api/src/main/java/org/apache/cloudstack/storage/sharedfs/SharedFSLifeCycle.java new file mode 100644 index 000000000000..552dcf79f78b --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/storage/sharedfs/SharedFSLifeCycle.java @@ -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. + +package org.apache.cloudstack.storage.sharedfs; + +import com.cloud.dc.DataCenter; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ManagementServerException; +import com.cloud.exception.OperationTimedoutException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.exception.VirtualMachineMigrationException; +import com.cloud.utils.Pair; + +public interface SharedFSLifeCycle { + void checkPrerequisites(DataCenter zone, Long serviceOfferingId); + + Pair deploySharedFS(SharedFS sharedFS, Long networkId, Long diskOfferingId, Long size, Long minIops, Long maxIops) throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, OperationTimedoutException; + + void startSharedFS(SharedFS sharedFS) throws OperationTimedoutException, ResourceUnavailableException, InsufficientCapacityException; + + boolean stopSharedFS(SharedFS sharedFS, Boolean forced); + + boolean deleteSharedFS(SharedFS sharedFS); + + boolean reDeploySharedFS(SharedFS sharedFS) throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, OperationTimedoutException; + + boolean changeSharedFSServiceOffering(SharedFS sharedFS, Long serviceOfferingId) throws ManagementServerException, ResourceUnavailableException, VirtualMachineMigrationException; +} diff --git a/api/src/main/java/org/apache/cloudstack/storage/sharedfs/SharedFSProvider.java b/api/src/main/java/org/apache/cloudstack/storage/sharedfs/SharedFSProvider.java new file mode 100644 index 000000000000..3966970f1887 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/storage/sharedfs/SharedFSProvider.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.storage.sharedfs; + +import com.cloud.utils.component.Adapter; + +public interface SharedFSProvider extends Adapter { + + enum SharedFSProviderType { + SHAREDFSVM + } + + void configure(); + + SharedFSLifeCycle getSharedFSLifeCycle(); +} 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 new file mode 100644 index 000000000000..21184de27a26 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/storage/sharedfs/SharedFSService.java @@ -0,0 +1,72 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.storage.sharedfs; + +import java.util.List; + +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.command.user.storage.sharedfs.ChangeSharedFSDiskOfferingCmd; +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 com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ManagementServerException; +import com.cloud.exception.OperationTimedoutException; +import com.cloud.exception.ResourceAllocationException; +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(); + + boolean stateTransitTo(SharedFS sharedFS, SharedFS.Event event); + + void setSharedFSProviders(List sharedFSProviders); + + SharedFSProvider getSharedFSProvider(String sharedFSProviderName); + + SharedFS allocSharedFS(CreateSharedFSCmd cmd); + + SharedFS deploySharedFS(CreateSharedFSCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, OperationTimedoutException; + + SharedFS startSharedFS(Long sharedFSId) throws OperationTimedoutException, ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException; + + SharedFS stopSharedFS(Long sharedFSId, Boolean forced); + + SharedFS restartSharedFS(Long sharedFSId, boolean cleanup) throws OperationTimedoutException, ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException; + + ListResponse searchForSharedFS(ResponseObject.ResponseView respView, ListSharedFSCmd cmd); + + SharedFS updateSharedFS(UpdateSharedFSCmd cmd); + + SharedFS changeSharedFSDiskOffering(ChangeSharedFSDiskOfferingCmd cmd) throws ResourceAllocationException; + + SharedFS changeSharedFSServiceOffering(ChangeSharedFSServiceOfferingCmd cmd) throws OperationTimedoutException, ResourceUnavailableException, InsufficientCapacityException, ManagementServerException, VirtualMachineMigrationException; + + Boolean destroySharedFS(DestroySharedFSCmd cmd); + + SharedFS recoverSharedFS(Long sharedFSId); + + void deleteSharedFS(Long sharedFSId); +} 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 new file mode 100644 index 000000000000..5f69f3e46e73 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageService.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.storage.volume; + +import com.cloud.hypervisor.Hypervisor; +import com.cloud.storage.Storage; +import com.cloud.utils.component.PluggableService; +import org.apache.cloudstack.api.command.admin.volume.ListVolumesForImportCmd; +import org.apache.cloudstack.api.command.admin.volume.ImportVolumeCmd; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.VolumeForImportResponse; +import org.apache.cloudstack.api.response.VolumeResponse; + +import java.util.Arrays; +import java.util.List; + +public interface VolumeImportUnmanageService extends PluggableService { + + 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); + + ListResponse listVolumesForImport(ListVolumesForImportCmd cmd); + + VolumeResponse importVolume(ImportVolumeCmd cmd); + + boolean unmanageVolume(long volumeId); + +} diff --git a/api/src/main/java/org/apache/cloudstack/storage/volume/VolumeOnStorageTO.java b/api/src/main/java/org/apache/cloudstack/storage/volume/VolumeOnStorageTO.java new file mode 100644 index 000000000000..1a8fd6ee273a --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/storage/volume/VolumeOnStorageTO.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.storage.volume; + +import com.cloud.hypervisor.Hypervisor; + +import java.util.HashMap; +import java.util.Map; + +public class VolumeOnStorageTO { + Hypervisor.HypervisorType hypervisorType; + private String path; + private String fullPath; + + private String name; + + private String format; + private long size; + private long virtualSize; + private String qemuEncryptFormat; + private Map details = new HashMap<>(); + + public enum Detail { + BACKING_FILE, BACKING_FILE_FORMAT, CLUSTER_SIZE, FILE_FORMAT, IS_LOCKED, IS_ENCRYPTED + } + + public VolumeOnStorageTO() { + } + + public VolumeOnStorageTO(Hypervisor.HypervisorType hypervisorType, String path, String name, String fullPath, String format, long size, long virtualSize) { + this.hypervisorType = hypervisorType; + this.path = path; + this.name = name; + this.fullPath = fullPath; + this.format = format; + this.size = size; + this.virtualSize = virtualSize; + } + + public Hypervisor.HypervisorType getHypervisorType() { + return hypervisorType; + } + + public void setHypervisorType(Hypervisor.HypervisorType hypervisorType) { + this.hypervisorType = hypervisorType; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getFullPath() { + return fullPath; + } + + public void setFullPath(String fullPath) { + this.fullPath = fullPath; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getFormat() { + return format; + } + + public void setFormat(String format) { + this.format = format; + } + + public long getSize() { + return size; + } + + public void setSize(long size) { + this.size = size; + } + + public long getVirtualSize() { + return virtualSize; + } + + public void setVirtualSize(long virtualSize) { + this.virtualSize = virtualSize; + } + + public String getQemuEncryptFormat() { + return qemuEncryptFormat; + } + + public void setQemuEncryptFormat(String qemuEncryptFormat) { + this.qemuEncryptFormat = qemuEncryptFormat; + } + + public Map getDetails() { + return details; + } + + public void setDetails(Map details) { + this.details = details; + } + + public void addDetail(Detail detail, String value) { + details.put(detail, value); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/usage/UsageService.java b/api/src/main/java/org/apache/cloudstack/usage/UsageService.java index 73962ba58752..00e8b431f8fe 100644 --- a/api/src/main/java/org/apache/cloudstack/usage/UsageService.java +++ b/api/src/main/java/org/apache/cloudstack/usage/UsageService.java @@ -20,7 +20,6 @@ import org.apache.cloudstack.api.command.admin.usage.GenerateUsageRecordsCmd; import org.apache.cloudstack.api.command.admin.usage.ListUsageRecordsCmd; import org.apache.cloudstack.api.command.admin.usage.RemoveRawUsageRecordsCmd; -import org.apache.cloudstack.api.response.UsageTypeResponse; import java.util.List; import java.util.TimeZone; @@ -62,6 +61,4 @@ public interface UsageService { TimeZone getUsageTimezone(); boolean removeRawUsageRecords(RemoveRawUsageRecordsCmd cmd); - - List listUsageTypes(); } diff --git a/api/src/main/java/org/apache/cloudstack/usage/UsageTypes.java b/api/src/main/java/org/apache/cloudstack/usage/UsageTypes.java index 5e0f03ff5830..5ad360a80262 100644 --- a/api/src/main/java/org/apache/cloudstack/usage/UsageTypes.java +++ b/api/src/main/java/org/apache/cloudstack/usage/UsageTypes.java @@ -46,32 +46,36 @@ public class UsageTypes { public static final int VM_SNAPSHOT_ON_PRIMARY = 27; public static final int BACKUP = 28; public static final int BUCKET = 29; + public static final int NETWORK = 30; + public static final int VPC = 31; public static List listUsageTypes() { List responseList = new ArrayList(); - responseList.add(new UsageTypeResponse(RUNNING_VM, "Running Vm Usage")); - responseList.add(new UsageTypeResponse(ALLOCATED_VM, "Allocated Vm Usage")); - responseList.add(new UsageTypeResponse(IP_ADDRESS, "IP Address Usage")); - responseList.add(new UsageTypeResponse(NETWORK_BYTES_SENT, "Network Usage (Bytes Sent)")); - responseList.add(new UsageTypeResponse(NETWORK_BYTES_RECEIVED, "Network Usage (Bytes Received)")); - responseList.add(new UsageTypeResponse(VOLUME, "Volume Usage")); - responseList.add(new UsageTypeResponse(TEMPLATE, "Template Usage")); - responseList.add(new UsageTypeResponse(ISO, "ISO Usage")); - responseList.add(new UsageTypeResponse(SNAPSHOT, "Snapshot Usage")); - responseList.add(new UsageTypeResponse(SECURITY_GROUP, "Security Group Usage")); - responseList.add(new UsageTypeResponse(LOAD_BALANCER_POLICY, "Load Balancer Usage")); - responseList.add(new UsageTypeResponse(PORT_FORWARDING_RULE, "Port Forwarding Usage")); - responseList.add(new UsageTypeResponse(NETWORK_OFFERING, "Network Offering Usage")); - responseList.add(new UsageTypeResponse(VPN_USERS, "VPN users usage")); - responseList.add(new UsageTypeResponse(VM_DISK_IO_READ, "VM Disk usage(I/O Read)")); - responseList.add(new UsageTypeResponse(VM_DISK_IO_WRITE, "VM Disk usage(I/O Write)")); - responseList.add(new UsageTypeResponse(VM_DISK_BYTES_READ, "VM Disk usage(Bytes Read)")); - responseList.add(new UsageTypeResponse(VM_DISK_BYTES_WRITE, "VM Disk usage(Bytes Write)")); - responseList.add(new UsageTypeResponse(VM_SNAPSHOT, "VM Snapshot storage usage")); - responseList.add(new UsageTypeResponse(VOLUME_SECONDARY, "Volume on secondary storage usage")); - responseList.add(new UsageTypeResponse(VM_SNAPSHOT_ON_PRIMARY, "VM Snapshot on primary storage usage")); - responseList.add(new UsageTypeResponse(BACKUP, "Backup storage usage")); - responseList.add(new UsageTypeResponse(BUCKET, "Bucket storage usage")); + responseList.add(new UsageTypeResponse(RUNNING_VM, "RUNNING_VM", "Running Vm Usage")); + responseList.add(new UsageTypeResponse(ALLOCATED_VM, "ALLOCATED_VM", "Allocated Vm Usage")); + responseList.add(new UsageTypeResponse(IP_ADDRESS, "IP_ADDRESS", "IP Address Usage")); + responseList.add(new UsageTypeResponse(NETWORK_BYTES_SENT, "NETWORK_BYTES_SENT", "Network Usage (Bytes Sent)")); + responseList.add(new UsageTypeResponse(NETWORK_BYTES_RECEIVED, "NETWORK_BYTES_RECEIVED", "Network Usage (Bytes Received)")); + responseList.add(new UsageTypeResponse(VOLUME, "VOLUME", "Volume Usage")); + responseList.add(new UsageTypeResponse(TEMPLATE, "TEMPLATE", "Template Usage")); + responseList.add(new UsageTypeResponse(ISO, "ISO", "ISO Usage")); + responseList.add(new UsageTypeResponse(SNAPSHOT, "SNAPSHOT", "Snapshot Usage")); + responseList.add(new UsageTypeResponse(SECURITY_GROUP, "SECURITY_GROUP", "Security Group Usage")); + responseList.add(new UsageTypeResponse(LOAD_BALANCER_POLICY, "LOAD_BALANCER_POLICY", "Load Balancer Usage")); + responseList.add(new UsageTypeResponse(PORT_FORWARDING_RULE, "PORT_FORWARDING_RULE", "Port Forwarding Usage")); + responseList.add(new UsageTypeResponse(NETWORK_OFFERING, "NETWORK_OFFERING", "Network Offering Usage")); + responseList.add(new UsageTypeResponse(VPN_USERS, "VPN_USERS", "VPN users usage")); + responseList.add(new UsageTypeResponse(VM_DISK_IO_READ, "VM_DISK_IO_READ", "VM Disk usage(I/O Read)")); + responseList.add(new UsageTypeResponse(VM_DISK_IO_WRITE, "VM_DISK_IO_WRITE", "VM Disk usage(I/O Write)")); + responseList.add(new UsageTypeResponse(VM_DISK_BYTES_READ, "VM_DISK_BYTES_READ", "VM Disk usage(Bytes Read)")); + responseList.add(new UsageTypeResponse(VM_DISK_BYTES_WRITE, "VM_DISK_BYTES_WRITE", "VM Disk usage(Bytes Write)")); + responseList.add(new UsageTypeResponse(VM_SNAPSHOT, "VM_SNAPSHOT", "VM Snapshot storage usage")); + responseList.add(new UsageTypeResponse(VOLUME_SECONDARY, "VOLUME_SECONDARY", "Volume on secondary storage usage")); + responseList.add(new UsageTypeResponse(VM_SNAPSHOT_ON_PRIMARY, "VM_SNAPSHOT_ON_PRIMARY", "VM Snapshot on primary storage usage")); + responseList.add(new UsageTypeResponse(BACKUP, "BACKUP", "Backup storage usage")); + responseList.add(new UsageTypeResponse(BUCKET, "BUCKET", "Bucket storage usage")); + responseList.add(new UsageTypeResponse(NETWORK, "NETWORK", "Network usage")); + responseList.add(new UsageTypeResponse(VPC, "VPC", "VPC usage")); return responseList; } } diff --git a/api/src/main/java/org/apache/cloudstack/user/ResourceReservation.java b/api/src/main/java/org/apache/cloudstack/user/ResourceReservation.java index 170193570cf1..d49120d44919 100644 --- a/api/src/main/java/org/apache/cloudstack/user/ResourceReservation.java +++ b/api/src/main/java/org/apache/cloudstack/user/ResourceReservation.java @@ -18,9 +18,12 @@ // package org.apache.cloudstack.user; -import com.cloud.configuration.Resource; import org.apache.cloudstack.api.InternalIdentity; +import com.cloud.configuration.Resource; + +import java.util.Date; + /** * an interface defining an {code}AutoClosable{code} reservation object */ @@ -33,5 +36,11 @@ Resource.ResourceType getResourceType(); + Long getResourceId(); + + String getTag(); + Long getReservedAmount(); + + Date getCreated(); } 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 4dfcd0a7de1b..7e718413118e 100644 --- a/api/src/main/java/org/apache/cloudstack/userdata/UserDataManager.java +++ b/api/src/main/java/org/apache/cloudstack/userdata/UserDataManager.java @@ -17,11 +17,28 @@ package org.apache.cloudstack.userdata; import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; 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", + "Max length of vm userdata after base64 encoding. Default is 32768 and maximum is 1048576", true); + 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 23e0e371714b..bba97dff71cb 100644 --- a/api/src/main/java/org/apache/cloudstack/vm/UnmanagedInstanceTO.java +++ b/api/src/main/java/org/apache/cloudstack/vm/UnmanagedInstanceTO.java @@ -17,6 +17,8 @@ package org.apache.cloudstack.vm; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; + import java.util.List; public class UnmanagedInstanceTO { @@ -31,6 +33,8 @@ public enum PowerState { private String internalCSName; + private String path; + private PowerState powerState; private PowerState cloneSourcePowerState; @@ -57,6 +61,9 @@ public enum PowerState { private String vncPassword; + private String bootType; + private String bootMode; + public String getName() { return name; } @@ -73,6 +80,14 @@ public void setInternalCSName(String internalCSName) { this.internalCSName = internalCSName; } + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + public PowerState getPowerState() { return powerState; } @@ -177,6 +192,29 @@ public void setVncPassword(String vncPassword) { this.vncPassword = vncPassword; } + @Override + public String toString() { + return String.format("UnmanagedInstanceTO %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "name", "internalCSName", "hostName", "clusterName")); + } + + public String getBootType() { + return bootType; + } + + public void setBootType(String bootType) { + this.bootType = bootType; + } + + public String getBootMode() { + return bootMode; + } + + public void setBootMode(String bootMode) { + this.bootMode = bootMode; + } + public static class Disk { private String diskId; @@ -317,6 +355,13 @@ public void setDatastorePort(int datastorePort) { public int getDatastorePort() { return datastorePort; } + + @Override + public String toString() { + return String.format("Disk %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "diskId", "internalCSName", "controller", "controllerUnit")); + } } public static class Nic { @@ -409,5 +454,12 @@ public String getPciSlot() { public void setPciSlot(String pciSlot) { this.pciSlot = pciSlot; } + + @Override + public String toString() { + return String.format("Nic %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "nicId", "adapterType", "macAddress")); + } } } 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 53aece949649..b6233b9c2704 100644 --- a/api/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManager.java +++ b/api/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManager.java @@ -30,6 +30,46 @@ public interface UnmanagedVMsManager extends VmImportService, UnmanageVMService, "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); + ConfigKey RemoteKvmInstanceDisksCopyTimeout = new ConfigKey<>(Integer.class, + "remote.kvm.instance.disks.copy.timeout", + "Advanced", + "30", + "Timeout (in mins) to prepare and copy the disks of remote KVM instance while importing the instance from an external host", + true, + ConfigKey.Scope.Global, + null); + + ConfigKey ConvertVmwareInstanceToKvmTimeout = new ConfigKey<>(Integer.class, + "convert.vmware.instance.to.kvm.timeout", + "Advanced", + "3", + "Timeout (in hours) for the instance conversion process from VMware through the virt-v2v binary on a KVM host", + true, + ConfigKey.Scope.Global, + null); + + ConfigKey ThreadsOnMSToImportVMwareVMFiles = new ConfigKey<>(Integer.class, + "threads.on.ms.to.import.vmware.vm.files", + "Advanced", + "0", + "Threads to use on the management server when importing VM files from VMWare." + + " -1 or 1 disables threads, 0 uses a thread per VM disk (disabled for single disk) and >1 for manual thread configuration." + + " Max number is 10, Default is 0.", + true, + ConfigKey.Scope.Global, + null); + + ConfigKey ThreadsOnKVMHostToImportVMwareVMFiles = new ConfigKey<>(Integer.class, + "threads.on.kvm.host.to.import.vmware.vm.files", + "Advanced", + "0", + "Threads to use on the KVM host (by the ovftool, if the version is 4.4.0+) when importing VM files from VMWare." + + " -1 or 1 disables threads, 0 uses a thread per VM disk (disabled for single disk) and >1 for manual thread configuration." + + " Max number is 10, Default is 0.", + true, + ConfigKey.Scope.Global, + null); + static boolean isSupported(Hypervisor.HypervisorType hypervisorType) { return hypervisorType == VMware || hypervisorType == KVM; } diff --git a/api/src/main/java/org/apache/cloudstack/vm/lease/VMLeaseManager.java b/api/src/main/java/org/apache/cloudstack/vm/lease/VMLeaseManager.java new file mode 100644 index 000000000000..b67026953059 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/vm/lease/VMLeaseManager.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.cloudstack.vm.lease; + +import com.cloud.utils.component.Manager; +import org.apache.cloudstack.framework.config.ConfigKey; + +import java.util.List; + +public interface VMLeaseManager extends Manager { + + int MAX_LEASE_DURATION_DAYS = 365_00; // 100 years + + enum ExpiryAction { + STOP, + DESTROY + } + + enum LeaseActionExecution { + PENDING, + DISABLED, + DONE, + CANCELLED + } + + ConfigKey InstanceLeaseEnabled = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, Boolean.class, + "instance.lease.enabled", "false", "Indicates whether to enable the Instance lease," + + " will be applicable only on instances created after lease is enabled. Disabling the feature cancels lease on existing instances with lease." + + " Re-enabling feature will not cause lease expiry actions on grandfathered instances", + true, List.of(ConfigKey.Scope.Global)); + + ConfigKey InstanceLeaseSchedulerInterval = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, Integer.class, + "instance.lease.scheduler.interval", "3600", "VM Lease Scheduler interval in seconds", + false, List.of(ConfigKey.Scope.Global)); + + ConfigKey InstanceLeaseExpiryEventSchedulerInterval = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, Integer.class, + "instance.lease.eventscheduler.interval", "86400", "Lease expiry event Scheduler interval in seconds", + false, List.of(ConfigKey.Scope.Global)); + + ConfigKey InstanceLeaseExpiryEventDaysBefore = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, Integer.class, + "instance.lease.expiryevent.daysbefore", "7", "Indicates how many days in advance, expiry events will be created before expiry.", + true, List.of(ConfigKey.Scope.Global)); + + void onLeaseFeatureToggle(); +} diff --git a/api/src/main/resources/META-INF/cloudstack/api-config/spring-api-config-context.xml b/api/src/main/resources/META-INF/cloudstack/api-config/spring-api-config-context.xml index 5ea32464ee9b..12d3c2361acd 100644 --- a/api/src/main/resources/META-INF/cloudstack/api-config/spring-api-config-context.xml +++ b/api/src/main/resources/META-INF/cloudstack/api-config/spring-api-config-context.xml @@ -28,5 +28,5 @@ > - + diff --git a/api/src/main/resources/META-INF/cloudstack/api-planner/spring-api-planner-context.xml b/api/src/main/resources/META-INF/cloudstack/api-planner/spring-api-planner-context.xml index 8523daf1d216..b614e362950b 100644 --- a/api/src/main/resources/META-INF/cloudstack/api-planner/spring-api-planner-context.xml +++ b/api/src/main/resources/META-INF/cloudstack/api-planner/spring-api-planner-context.xml @@ -30,5 +30,5 @@ - + 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/agent/api/to/LoadBalancerTOTest.java b/api/src/test/java/com/cloud/agent/api/to/LoadBalancerTOTest.java index b12c1b81d4a3..e7ecbebae7bd 100644 --- a/api/src/test/java/com/cloud/agent/api/to/LoadBalancerTOTest.java +++ b/api/src/test/java/com/cloud/agent/api/to/LoadBalancerTOTest.java @@ -41,16 +41,19 @@ public class LoadBalancerTOTest { LoadBalancerTO.AutoScaleVmGroupTO vmGroup; private static final Long counterId = 1L; + private static final String counterUuid = "1111-1111-1100"; private static final String counterName = "counter name"; private static final Counter.Source counterSource = Counter.Source.CPU; private static final String counterValue = "counter value"; private static final String counterProvider = "VIRTUALROUTER"; private static final Long conditionId = 2L; + private static final String conditionUuid = "1111-1111-1110"; private static final Long threshold = 100L; private static final Condition.Operator relationalOperator = Condition.Operator.GT; private static final Long scaleUpPolicyId = 11L; + private static final String scaleUpPolicyUuid = "1111-1111-1111"; private static final int scaleUpPolicyDuration = 61; private static final int scaleUpPolicyQuietTime = 31; private static final Date scaleUpPolicyLastQuietTime = new Date(); @@ -85,14 +88,14 @@ public class LoadBalancerTOTest { @Before public void setUp() { - counter = new LoadBalancerTO.CounterTO(counterId, counterName, counterSource, counterValue, counterProvider); - condition = new LoadBalancerTO.ConditionTO(conditionId, threshold, relationalOperator, counter); - scaleUpPolicy = new LoadBalancerTO.AutoScalePolicyTO(scaleUpPolicyId, scaleUpPolicyDuration, scaleUpPolicyQuietTime, - scaleUpPolicyLastQuietTime, AutoScalePolicy.Action.SCALEUP, - Arrays.asList(new LoadBalancerTO.ConditionTO[]{ condition }), false); - scaleDownPolicy = new LoadBalancerTO.AutoScalePolicyTO(scaleDownPolicyId, scaleDownPolicyDuration, scaleDownPolicyQuietTime, - scaleDownPolicyLastQuietTime, AutoScalePolicy.Action.SCALEDOWN, - Arrays.asList(new LoadBalancerTO.ConditionTO[]{ condition }), false); + counter = new LoadBalancerTO.CounterTO(counterId, counterUuid, counterName, counterSource, counterValue, counterProvider); + condition = new LoadBalancerTO.ConditionTO(conditionId, conditionUuid, threshold, relationalOperator, counter); + scaleUpPolicy = new LoadBalancerTO.AutoScalePolicyTO(scaleUpPolicyId, scaleUpPolicyUuid, scaleUpPolicyDuration, + scaleUpPolicyQuietTime, scaleUpPolicyLastQuietTime, + AutoScalePolicy.Action.SCALEUP, Arrays.asList(new LoadBalancerTO.ConditionTO[]{ condition }), false); + scaleDownPolicy = new LoadBalancerTO.AutoScalePolicyTO(scaleDownPolicyId, scaleUpPolicyUuid, scaleDownPolicyDuration, + scaleDownPolicyQuietTime, scaleDownPolicyLastQuietTime, + AutoScalePolicy.Action.SCALEDOWN, Arrays.asList(new LoadBalancerTO.ConditionTO[]{ condition }), false); vmProfile = new LoadBalancerTO.AutoScaleVmProfileTO(zoneId, domainId, cloudStackApiUrl, autoScaleUserApiKey, autoScaleUserSecretKey, serviceOfferingId, templateId, vmName, networkId, otherDeployParams, counterParamList, expungeVmGracePeriod); @@ -113,6 +116,7 @@ public void testCounterTO() { @Test public void testConditionTO() { Assert.assertEquals(conditionId, condition.getId()); + Assert.assertEquals(conditionUuid, condition.getUuid()); Assert.assertEquals((long) threshold, condition.getThreshold()); Assert.assertEquals(relationalOperator, condition.getRelationalOperator()); Assert.assertEquals(counter, condition.getCounter()); diff --git a/api/src/test/java/com/cloud/cpu/CPUTest.java b/api/src/test/java/com/cloud/cpu/CPUTest.java new file mode 100644 index 000000000000..0a059cf9a90a --- /dev/null +++ b/api/src/test/java/com/cloud/cpu/CPUTest.java @@ -0,0 +1,70 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.cpu; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class CPUTest { + @Test + public void testCPUArchGetType() { + assertEquals("i686", CPU.CPUArch.x86.getType()); + assertEquals("x86_64", CPU.CPUArch.amd64.getType()); + assertEquals("aarch64", CPU.CPUArch.arm64.getType()); + assertEquals("s390x", CPU.CPUArch.s390x.getType()); + } + + @Test + 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 + 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 + public void testCPUArchFromTypeWithDefaultForBlankOrNull() { + assertEquals(CPU.CPUArch.amd64, CPU.CPUArch.fromType("")); + assertEquals(CPU.CPUArch.amd64, CPU.CPUArch.fromType(" ")); + assertEquals(CPU.CPUArch.amd64, CPU.CPUArch.fromType(null)); + } + + @Test + public void testCPUArchFromTypeWithInvalidValue() { + Exception exception = assertThrows(IllegalArgumentException.class, () -> { + CPU.CPUArch.fromType("unsupported"); + }); + assertTrue(exception.getMessage().contains("Unsupported arch type: unsupported")); + } + + @Test + public void testCPUArchGetTypesAsCSV() { + String expectedCSV = "i686,x86_64,aarch64,s390x"; + assertEquals(expectedCSV, CPU.CPUArch.getTypesAsCSV()); + } +} 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/bgp/CreateASNRangeCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/bgp/CreateASNRangeCmdTest.java new file mode 100644 index 000000000000..603cda2040d0 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/bgp/CreateASNRangeCmdTest.java @@ -0,0 +1,69 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.bgp; + +import com.cloud.bgp.ASNumberRange; +import com.cloud.bgp.BGPService; + +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.response.ASNRangeResponse; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class CreateASNRangeCmdTest { + + BGPService bgpService = Mockito.spy(BGPService.class); + ResponseGenerator _responseGenerator = Mockito.spy(ResponseGenerator.class); + + @Test + public void testCreateASNRangeCmd() { + Long zoneId = 1L; + Long startASNumber = 110000L; + Long endASNumber = 120000L; + + CreateASNRangeCmd cmd = new CreateASNRangeCmd(); + ReflectionTestUtils.setField(cmd, "zoneId", zoneId); + ReflectionTestUtils.setField(cmd, "startASNumber", startASNumber); + ReflectionTestUtils.setField(cmd, "endASNumber", endASNumber); + ReflectionTestUtils.setField(cmd,"bgpService", bgpService); + ReflectionTestUtils.setField(cmd,"_responseGenerator", _responseGenerator); + + Assert.assertEquals(zoneId, cmd.getZoneId()); + Assert.assertEquals(startASNumber, cmd.getStartASNumber()); + Assert.assertEquals(endASNumber, cmd.getEndASNumber()); + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + + ASNumberRange asnRange = Mockito.mock(ASNumberRange.class); + Mockito.when(bgpService.createASNumberRange(zoneId, startASNumber, endASNumber)).thenReturn(asnRange); + + ASNRangeResponse response = Mockito.mock(ASNRangeResponse.class); + Mockito.when(_responseGenerator.createASNumberRangeResponse(asnRange)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(response, cmd.getResponseObject()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/bgp/DeleteASNRangeCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/bgp/DeleteASNRangeCmdTest.java new file mode 100644 index 000000000000..2abcf736c5b5 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/bgp/DeleteASNRangeCmdTest.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.bgp; + +import com.cloud.bgp.BGPService; + +import org.apache.cloudstack.api.response.SuccessResponse; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class DeleteASNRangeCmdTest { + + BGPService bgpService = Mockito.spy(BGPService.class); + + @Test + public void testDeleteASNRangeCmd() { + Long id = 200L; + + DeleteASNRangeCmd cmd = new DeleteASNRangeCmd(); + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd,"bgpService", bgpService); + + Assert.assertEquals(id, cmd.getId()); + + Mockito.when(bgpService.deleteASRange(id)).thenReturn(true); + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Object response = cmd.getResponseObject(); + Assert.assertTrue(response instanceof SuccessResponse); + + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/bgp/ListASNRangesCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/bgp/ListASNRangesCmdTest.java new file mode 100644 index 000000000000..7f49c61a6936 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/bgp/ListASNRangesCmdTest.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.bgp; + +import com.cloud.bgp.ASNumberRange; +import com.cloud.bgp.BGPService; + +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.response.ASNRangeResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(MockitoJUnitRunner.class) +public class ListASNRangesCmdTest { + + BGPService bgpService = Mockito.spy(BGPService.class); + ResponseGenerator _responseGenerator = Mockito.spy(ResponseGenerator.class); + + @Test + public void testListASNRangesCmdTest() { + Long zoneId = 1L; + + ListASNRangesCmd cmd = new ListASNRangesCmd(); + ReflectionTestUtils.setField(cmd, "zoneId", zoneId); + ReflectionTestUtils.setField(cmd,"bgpService", bgpService); + ReflectionTestUtils.setField(cmd,"_responseGenerator", _responseGenerator); + + Assert.assertEquals(zoneId, cmd.getZoneId()); + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + + List ranges = new ArrayList<>(); + ASNumberRange asnRange = Mockito.mock(ASNumberRange.class); + ranges.add(asnRange); + + ASNRangeResponse asnRangeResponse = Mockito.mock(ASNRangeResponse.class); + Mockito.when(_responseGenerator.createASNumberRangeResponse(asnRange)).thenReturn(asnRangeResponse); + + Mockito.when(bgpService.listASNumberRanges(zoneId)).thenReturn(ranges); + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Object response = cmd.getResponseObject(); + Assert.assertTrue(response instanceof ListResponse); + ListResponse listResponse = (ListResponse) response; + Assert.assertEquals(1L, (long) listResponse.getCount()); + Assert.assertTrue(listResponse.getResponses().get(0) instanceof ASNRangeResponse); + ASNRangeResponse firstResponse = (ASNRangeResponse) listResponse.getResponses().get(0); + Assert.assertEquals(asnRangeResponse, firstResponse); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/bgp/ReleaseASNumberCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/bgp/ReleaseASNumberCmdTest.java new file mode 100644 index 000000000000..1b80e5bff7f0 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/bgp/ReleaseASNumberCmdTest.java @@ -0,0 +1,61 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.bgp; + +import com.cloud.bgp.BGPService; +import com.cloud.utils.Pair; + +import org.apache.cloudstack.api.response.SuccessResponse; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class ReleaseASNumberCmdTest { + + BGPService bgpService = Mockito.spy(BGPService.class); + + @Test + public void testReleaseASNumberCmd() { + Long zoneId = 1L; + Long asNumber = 10000L; + + ReleaseASNumberCmd cmd = new ReleaseASNumberCmd(); + ReflectionTestUtils.setField(cmd, "zoneId", zoneId); + ReflectionTestUtils.setField(cmd, "asNumber", asNumber); + ReflectionTestUtils.setField(cmd,"bgpService", bgpService); + + Assert.assertEquals(zoneId, cmd.getZoneId()); + Assert.assertEquals(asNumber, cmd.getAsNumber()); + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + + Pair resultPair = Mockito.mock(Pair.class); + Mockito.when(resultPair.first()).thenReturn(true); + Mockito.when(bgpService.releaseASNumber(zoneId, asNumber, false)).thenReturn(resultPair); + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Object response = cmd.getResponseObject(); + Assert.assertTrue(response instanceof SuccessResponse); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/cluster/ListClustersCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/cluster/ListClustersCmdTest.java new file mode 100644 index 000000000000..af53a539e670 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/cluster/ListClustersCmdTest.java @@ -0,0 +1,83 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.cluster; + +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.apache.cloudstack.api.response.ClusterResponse; +import org.apache.cloudstack.extension.Extension; +import org.apache.cloudstack.extension.ExtensionHelper; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import com.cloud.hypervisor.Hypervisor; + +@RunWith(MockitoJUnitRunner.class) +public class ListClustersCmdTest { + + ExtensionHelper extensionHelper; + ListClustersCmd listClustersCmd = new ListClustersCmd(); + + @Before + public void setUp() { + extensionHelper = mock(ExtensionHelper.class); + listClustersCmd.extensionHelper = extensionHelper; + } + + @Test + public void updateClustersExtensions_emptyList_noAction() { + listClustersCmd.updateClustersExtensions(Collections.emptyList()); + // No exception, nothing to verify + } + + @Test + public void updateClustersExtensions_nullList_noAction() { + listClustersCmd.updateClustersExtensions(null); + // No exception, nothing to verify + } + + @Test + public void updateClustersExtensions_withClusterResponses_setsExtension() { + ClusterResponse cluster1 = mock(ClusterResponse.class); + ClusterResponse cluster2 = mock(ClusterResponse.class); + when(cluster1.getInternalId()).thenReturn(1L); + when(cluster1.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.External.name()); + when(cluster2.getInternalId()).thenReturn(2L); + when(cluster2.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.External.name()); + Extension ext1 = mock(Extension.class); + when(ext1.getUuid()).thenReturn("a"); + Extension ext2 = mock(Extension.class); + when(ext2.getUuid()).thenReturn("b"); + when(extensionHelper.getExtensionIdForCluster(anyLong())).thenAnswer(invocation -> invocation.getArguments()[0]); + when(extensionHelper.getExtension(1L)).thenReturn(ext1); + when(extensionHelper.getExtension(2L)).thenReturn(ext2); + List clusters = Arrays.asList(cluster1, cluster2); + listClustersCmd.updateClustersExtensions(clusters); + verify(cluster1).setExtensionId("a"); + verify(cluster2).setExtensionId("b"); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/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/domain/ListDomainsCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/domain/ListDomainsCmdTest.java new file mode 100644 index 000000000000..45f175e9a815 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/domain/ListDomainsCmdTest.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.admin.domain; + +import java.util.List; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.response.DomainResponse; +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; + +import com.cloud.user.ResourceLimitService; + +@RunWith(MockitoJUnitRunner.class) +public class ListDomainsCmdTest { + + @Mock + ResourceLimitService resourceLimitService; + + + @Test + public void testGetShowIcon() { + ListDomainsCmd cmd = new ListDomainsCmd(); + ReflectionTestUtils.setField(cmd, "showIcon", null); + Assert.assertFalse(cmd.getShowIcon()); + ReflectionTestUtils.setField(cmd, "showIcon", false); + Assert.assertFalse(cmd.getShowIcon()); + ReflectionTestUtils.setField(cmd, "showIcon", true); + Assert.assertTrue(cmd.getShowIcon()); + } + + @Test + public void testGetTag() { + ListDomainsCmd cmd = new ListDomainsCmd(); + ReflectionTestUtils.setField(cmd, "tag", null); + Assert.assertNull(cmd.getTag()); + String tag = "ABC"; + ReflectionTestUtils.setField(cmd, "tag", tag); + Assert.assertEquals(tag, cmd.getTag()); + } + + @Test + public void testUpdateDomainResponseNoDomains() { + ListDomainsCmd cmd = new ListDomainsCmd(); + cmd._resourceLimitService = resourceLimitService; + cmd.updateDomainResponse(null); + Mockito.verify(resourceLimitService, Mockito.never()).updateTaggedResourceLimitsAndCountsForDomains(Mockito.anyList(), Mockito.anyString()); + } + + @Test + public void testUpdateDomainResponseWithDomains() { + ListDomainsCmd cmd = new ListDomainsCmd(); + cmd._resourceLimitService = resourceLimitService; + ReflectionTestUtils.setField(cmd, "tag", "abc"); + cmd.updateDomainResponse(List.of(Mockito.mock(DomainResponse.class))); + Mockito.verify(resourceLimitService).updateTaggedResourceLimitsAndCountsForDomains(Mockito.any(), Mockito.any()); + } + + @Test + public void testUpdateDomainResponseWithDomainsMinDetails() { + ListDomainsCmd cmd = new ListDomainsCmd(); + ReflectionTestUtils.setField(cmd, "viewDetails", List.of(ApiConstants.DomainDetails.min.toString())); + cmd._resourceLimitService = resourceLimitService; + ReflectionTestUtils.setField(cmd, "tag", "abc"); + cmd.updateDomainResponse(List.of(Mockito.mock(DomainResponse.class))); + Mockito.verify(resourceLimitService, Mockito.never()).updateTaggedResourceLimitsAndCountsForDomains(Mockito.any(), Mockito.any()); + } + +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuCardCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuCardCmdTest.java new file mode 100644 index 000000000000..be21384109c2 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuCardCmdTest.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class CreateGpuCardCmdTest { + + @Test + public void getDeviceId() { + CreateGpuCardCmd cmd = new CreateGpuCardCmd(); + assertNull(cmd.getDeviceId()); + String deviceId = "0000:00:1f.6"; + ReflectionTestUtils.setField(cmd, "deviceId", deviceId); + assertEquals(deviceId, cmd.getDeviceId()); + } + + @Test + public void getDeviceName() { + CreateGpuCardCmd cmd = new CreateGpuCardCmd(); + assertNull(cmd.getDeviceName()); + String deviceName = "NVIDIA GeForce GTX 1080"; + ReflectionTestUtils.setField(cmd, "deviceName", deviceName); + assertEquals(deviceName, cmd.getDeviceName()); + } + + @Test + public void getName() { + CreateGpuCardCmd cmd = new CreateGpuCardCmd(); + assertNull(cmd.getName()); + String name = "Test GPU Card"; + ReflectionTestUtils.setField(cmd, "name", name); + assertEquals(name, cmd.getName()); + } + + @Test + public void getVendorName() { + CreateGpuCardCmd cmd = new CreateGpuCardCmd(); + assertNull(cmd.getVendorName()); + String vendorName = "NVIDIA"; + ReflectionTestUtils.setField(cmd, "vendorName", vendorName); + assertEquals(vendorName, cmd.getVendorName()); + } + + @Test + public void getVendorId() { + CreateGpuCardCmd cmd = new CreateGpuCardCmd(); + assertNull(cmd.getVendorId()); + String vendorId = "10de"; // NVIDIA vendor ID + ReflectionTestUtils.setField(cmd, "vendorId", vendorId); + assertEquals(vendorId, cmd.getVendorId()); + } + + @Test + public void getVideoRam() { + CreateGpuCardCmd cmd = new CreateGpuCardCmd(); + assertNull(cmd.getVideoRam()); + Long videoRam = 8192L; // 8 GB + ReflectionTestUtils.setField(cmd, "videoRam", videoRam); + assertEquals(videoRam, cmd.getVideoRam()); + } + + @Test + public void getEntityOwnerId() { + CreateGpuCardCmd cmd = new CreateGpuCardCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuDeviceCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuDeviceCmdTest.java new file mode 100644 index 000000000000..fd5c568d5bc5 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuDeviceCmdTest.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.apache.cloudstack.gpu.GpuDevice; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class CreateGpuDeviceCmdTest { + + @Test + public void getHostId() { + CreateGpuDeviceCmd cmd = new CreateGpuDeviceCmd(); + assertNull(cmd.getHostId()); + Long hostId = 1L; + ReflectionTestUtils.setField(cmd, "hostId", hostId); + assertEquals(hostId, cmd.getHostId()); + } + + @Test + public void getBusAddress() { + CreateGpuDeviceCmd cmd = new CreateGpuDeviceCmd(); + assertNull(cmd.getBusAddress()); + String busAddress = "0000:00:1f.6"; + ReflectionTestUtils.setField(cmd, "busAddress", busAddress); + assertEquals(busAddress, cmd.getBusAddress()); + } + + @Test + public void getGpuCardId() { + CreateGpuDeviceCmd cmd = new CreateGpuDeviceCmd(); + assertNull(cmd.getGpuCardId()); + Long gpuCardId = 1L; + ReflectionTestUtils.setField(cmd, "gpuCardId", gpuCardId); + assertEquals(gpuCardId, cmd.getGpuCardId()); + } + + @Test + public void getVgpuProfileId() { + CreateGpuDeviceCmd cmd = new CreateGpuDeviceCmd(); + assertNull(cmd.getVgpuProfileId()); + Long vgpuProfileId = 1L; + ReflectionTestUtils.setField(cmd, "vgpuProfileId", vgpuProfileId); + assertEquals(vgpuProfileId, cmd.getVgpuProfileId()); + } + + @Test + public void getType() { + CreateGpuDeviceCmd cmd = new CreateGpuDeviceCmd(); + assertEquals(GpuDevice.DeviceType.PCI, cmd.getType()); + String type = "MDEV"; + ReflectionTestUtils.setField(cmd, "type", type); + assertEquals(GpuDevice.DeviceType.MDEV, cmd.getType()); + } + + @Test + public void getParentGpuDeviceId() { + CreateGpuDeviceCmd cmd = new CreateGpuDeviceCmd(); + assertNull(cmd.getParentGpuDeviceId()); + Long parentGpuDeviceId = 1L; + ReflectionTestUtils.setField(cmd, "parentGpuDeviceId", parentGpuDeviceId); + assertEquals(parentGpuDeviceId, cmd.getParentGpuDeviceId()); + } + + @Test + public void getNumaNode() { + CreateGpuDeviceCmd cmd = new CreateGpuDeviceCmd(); + assertEquals("-1", cmd.getNumaNode()); + String numaNode = "0"; + ReflectionTestUtils.setField(cmd, "numaNode", numaNode); + assertEquals(numaNode, cmd.getNumaNode()); + } + + @Test + public void getEntityOwnerId() { + CreateGpuDeviceCmd cmd = new CreateGpuDeviceCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/CreateVgpuProfileCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/CreateVgpuProfileCmdTest.java new file mode 100644 index 000000000000..c71286bda652 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/CreateVgpuProfileCmdTest.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class CreateVgpuProfileCmdTest { + + @Test + public void getName() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertNull(cmd.getName()); + String name = "Test VGPU Profile"; + ReflectionTestUtils.setField(cmd, "name", name); + assertEquals(name, cmd.getName()); + } + + @Test + public void getDescription() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertNull(cmd.getDescription()); + String description = "Test VGPU Profile Description"; + ReflectionTestUtils.setField(cmd, "description", description); + assertEquals(description, cmd.getDescription()); + } + + @Test + public void getCardId() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertNull(cmd.getCardId()); + Long cardId = 1L; + ReflectionTestUtils.setField(cmd, "cardId", cardId); + assertEquals(cardId, cmd.getCardId()); + } + + @Test + public void getMaxVgpuPerPgpu() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertNull(cmd.getMaxVgpuPerPgpu()); + Long maxVgpuPerPgpu = 8L; + ReflectionTestUtils.setField(cmd, "maxVgpuPerPgpu", maxVgpuPerPgpu); + assertEquals(maxVgpuPerPgpu, cmd.getMaxVgpuPerPgpu()); + } + + @Test + public void getVideoRam() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertNull(cmd.getVideoRam()); + Long videoRam = 8192L; // 8 GB + ReflectionTestUtils.setField(cmd, "videoRam", videoRam); + assertEquals(videoRam, cmd.getVideoRam()); + } + + @Test + public void getMaxHeads() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertNull(cmd.getMaxHeads()); + Long maxHeads = 2L; + ReflectionTestUtils.setField(cmd, "maxHeads", maxHeads); + assertEquals(maxHeads, cmd.getMaxHeads()); + } + + @Test + public void getMaxResolutionX() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertNull(cmd.getMaxResolutionX()); + Long maxResolutionX = 1920L; // 1920 pixels + ReflectionTestUtils.setField(cmd, "maxResolutionX", maxResolutionX); + assertEquals(maxResolutionX, cmd.getMaxResolutionX()); + } + + @Test + public void getMaxResolutionY() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertNull(cmd.getMaxResolutionY()); + Long maxResolutionY = 1080L; // 1080 pixels + ReflectionTestUtils.setField(cmd, "maxResolutionY", maxResolutionY); + assertEquals(maxResolutionY, cmd.getMaxResolutionY()); + } + + @Test + public void getEntityOwnerId() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuCardCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuCardCmdTest.java new file mode 100644 index 000000000000..21df915b4209 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuCardCmdTest.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class DeleteGpuCardCmdTest { + + @Test + public void getId() { + DeleteGpuCardCmd cmd = new DeleteGpuCardCmd(); + assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + assertEquals(id, cmd.getId()); + } + + @Test + public void getEntityOwnerId() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuDeviceCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuDeviceCmdTest.java new file mode 100644 index 000000000000..02b04dd307a8 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuDeviceCmdTest.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class DeleteGpuDeviceCmdTest { + + @Test + public void getIds() { + DeleteGpuDeviceCmd cmd = new DeleteGpuDeviceCmd(); + assertNull(cmd.getIds()); + List ids = List.of(1L, 2L, 3L); + ReflectionTestUtils.setField(cmd, "ids", ids); + assertEquals(ids, cmd.getIds()); + } + + @Test + public void getEntityOwnerId() { + DeleteGpuDeviceCmd cmd = new DeleteGpuDeviceCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteVgpuProfileCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteVgpuProfileCmdTest.java new file mode 100644 index 000000000000..ecd43810e65c --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteVgpuProfileCmdTest.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class DeleteVgpuProfileCmdTest { + + @Test + public void getId() { + DeleteVgpuProfileCmd cmd = new DeleteVgpuProfileCmd(); + assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + assertEquals(id, cmd.getId()); + } + + @Test + public void getEntityOwnerId() { + DeleteVgpuProfileCmd cmd = new DeleteVgpuProfileCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DiscoverGpuDevicesCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DiscoverGpuDevicesCmdTest.java new file mode 100644 index 000000000000..8295e06e0d55 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DiscoverGpuDevicesCmdTest.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.admin.gpu; + +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class DiscoverGpuDevicesCmdTest { + + @Test + public void getId() { + DiscoverGpuDevicesCmd cmd = new DiscoverGpuDevicesCmd(); + assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + assertEquals(id, cmd.getId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/ListGpuDevicesCmdByAdminTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/ListGpuDevicesCmdByAdminTest.java new file mode 100644 index 000000000000..200bce769336 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/ListGpuDevicesCmdByAdminTest.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.admin.gpu; + +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class ListGpuDevicesCmdByAdminTest { + + @Test + public void getId() { + ListGpuDevicesCmdByAdmin cmd = new ListGpuDevicesCmdByAdmin(); + assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + assertEquals(id, cmd.getId()); + } + + @Test + public void getHostId() { + ListGpuDevicesCmdByAdmin cmd = new ListGpuDevicesCmdByAdmin(); + assertNull(cmd.getHostId()); + Long hostId = 1L; + ReflectionTestUtils.setField(cmd, "hostId", hostId); + assertEquals(hostId, cmd.getHostId()); + } + + @Test + public void getGpuCardId() { + ListGpuDevicesCmdByAdmin cmd = new ListGpuDevicesCmdByAdmin(); + assertNull(cmd.getGpuCardId()); + Long gpuCardId = 1L; + ReflectionTestUtils.setField(cmd, "gpuCardId", gpuCardId); + assertEquals(gpuCardId, cmd.getGpuCardId()); + } + + @Test + public void getVgpuProfileId() { + ListGpuDevicesCmdByAdmin cmd = new ListGpuDevicesCmdByAdmin(); + assertNull(cmd.getVgpuProfileId()); + Long vgpuProfileId = 1L; + ReflectionTestUtils.setField(cmd, "vgpuProfileId", vgpuProfileId); + assertEquals(vgpuProfileId, cmd.getVgpuProfileId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/ManageGpuDeviceCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/ManageGpuDeviceCmdTest.java new file mode 100644 index 000000000000..ee862409a939 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/ManageGpuDeviceCmdTest.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class ManageGpuDeviceCmdTest { + + @Test + public void getIds() { + ManageGpuDeviceCmd cmd = new ManageGpuDeviceCmd(); + assertNull(cmd.getIds()); + List ids = List.of(1L, 2L, 3L); + ReflectionTestUtils.setField(cmd, "ids", ids); + assertEquals(ids, cmd.getIds()); + } + + @Test + public void getEntityOwnerId() { + ManageGpuDeviceCmd cmd = new ManageGpuDeviceCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UnmanageGpuDeviceCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UnmanageGpuDeviceCmdTest.java new file mode 100644 index 000000000000..63700d9e908d --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UnmanageGpuDeviceCmdTest.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class UnmanageGpuDeviceCmdTest { + + @Test + public void getIds() { + UnmanageGpuDeviceCmd cmd = new UnmanageGpuDeviceCmd(); + assertNull(cmd.getIds()); + List ids = List.of(1L, 2L, 3L); + ReflectionTestUtils.setField(cmd, "ids", ids); + assertEquals(ids, cmd.getIds()); + } + + @Test + public void getEntityOwnerId() { + UnmanageGpuDeviceCmd cmd = new UnmanageGpuDeviceCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuCardCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuCardCmdTest.java new file mode 100644 index 000000000000..ead7ab9d3d30 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuCardCmdTest.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class UpdateGpuCardCmdTest { + + @Test + public void getId() { + UpdateGpuCardCmd cmd = new UpdateGpuCardCmd(); + assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + assertEquals(id, cmd.getId()); + } + + @Test + public void getDeviceName() { + UpdateGpuCardCmd cmd = new UpdateGpuCardCmd(); + assertNull(cmd.getDeviceName()); + String deviceName = "GPU-1234"; + ReflectionTestUtils.setField(cmd, "deviceName", deviceName); + assertEquals(deviceName, cmd.getDeviceName()); + } + + @Test + public void getName() { + UpdateGpuCardCmd cmd = new UpdateGpuCardCmd(); + assertNull(cmd.getName()); + String name = "Test GPU Card"; + ReflectionTestUtils.setField(cmd, "name", name); + assertEquals(name, cmd.getName()); + } + + @Test + public void getVendorName() { + UpdateGpuCardCmd cmd = new UpdateGpuCardCmd(); + assertNull(cmd.getVendorName()); + String vendorName = "NVIDIA"; + ReflectionTestUtils.setField(cmd, "vendorName", vendorName); + assertEquals(vendorName, cmd.getVendorName()); + } + + @Test + public void getEntityOwnerId() { + UpdateGpuCardCmd cmd = new UpdateGpuCardCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuDeviceCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuDeviceCmdTest.java new file mode 100644 index 000000000000..6ebec48aa003 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuDeviceCmdTest.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.apache.cloudstack.gpu.GpuDevice; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class UpdateGpuDeviceCmdTest { + + @Test + public void getId() { + UpdateGpuDeviceCmd cmd = new UpdateGpuDeviceCmd(); + assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + assertEquals(id, cmd.getId()); + } + + @Test + public void getGpuCardId() { + UpdateGpuDeviceCmd cmd = new UpdateGpuDeviceCmd(); + assertNull(cmd.getGpuCardId()); + Long gpuCardId = 1L; + ReflectionTestUtils.setField(cmd, "gpuCardId", gpuCardId); + assertEquals(gpuCardId, cmd.getGpuCardId()); + } + + @Test + public void getVgpuProfileId() { + UpdateGpuDeviceCmd cmd = new UpdateGpuDeviceCmd(); + assertNull(cmd.getVgpuProfileId()); + Long vgpuProfileId = 1L; + ReflectionTestUtils.setField(cmd, "vgpuProfileId", vgpuProfileId); + assertEquals(vgpuProfileId, cmd.getVgpuProfileId()); + } + + @Test + public void getType() { + UpdateGpuDeviceCmd cmd = new UpdateGpuDeviceCmd(); + assertNull(cmd.getType()); + String type = "MDEV"; + ReflectionTestUtils.setField(cmd, "type", type); + assertEquals(GpuDevice.DeviceType.MDEV, cmd.getType()); + } + + @Test + public void getParentGpuDeviceId() { + UpdateGpuDeviceCmd cmd = new UpdateGpuDeviceCmd(); + assertNull(cmd.getParentGpuDeviceId()); + Long parentGpuDeviceId = 1L; + ReflectionTestUtils.setField(cmd, "parentGpuDeviceId", parentGpuDeviceId); + assertEquals(parentGpuDeviceId, cmd.getParentGpuDeviceId()); + } + + @Test + public void getNumaNode() { + UpdateGpuDeviceCmd cmd = new UpdateGpuDeviceCmd(); + assertNull(cmd.getNumaNode()); + String numaNode = "0"; + ReflectionTestUtils.setField(cmd, "numaNode", numaNode); + assertEquals(numaNode, cmd.getNumaNode()); + } + + @Test + public void getEntityOwnerId() { + UpdateGpuDeviceCmd cmd = new UpdateGpuDeviceCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateVgpuProfileCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateVgpuProfileCmdTest.java new file mode 100644 index 000000000000..95acd71096e9 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateVgpuProfileCmdTest.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.admin.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class UpdateVgpuProfileCmdTest { + + @Test + public void getId() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + assertEquals(id, cmd.getId()); + } + + @Test + public void getProfileName() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertNull(cmd.getProfileName()); + String profileName = "Test VGPU Profile"; + ReflectionTestUtils.setField(cmd, "profileName", profileName); + assertEquals(profileName, cmd.getProfileName()); + } + + @Test + public void getDescription() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertNull(cmd.getDescription()); + String description = "Test VGPU Profile Description"; + ReflectionTestUtils.setField(cmd, "description", description); + assertEquals(description, cmd.getDescription()); + } + + @Test + public void getMaxVgpuPerPgpu() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertNull(cmd.getMaxVgpuPerPgpu()); + Long maxVgpuPerPgpu = 8L; + ReflectionTestUtils.setField(cmd, "maxVgpuPerPgpu", maxVgpuPerPgpu); + assertEquals(maxVgpuPerPgpu, cmd.getMaxVgpuPerPgpu()); + } + + @Test + public void getVideoRam() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertNull(cmd.getVideoRam()); + Long videoRam = 8192L; // 8 GB + ReflectionTestUtils.setField(cmd, "videoRam", videoRam); + assertEquals(videoRam, cmd.getVideoRam()); + } + + @Test + public void getMaxHeads() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertNull(cmd.getMaxHeads()); + Long maxHeads = 2L; + ReflectionTestUtils.setField(cmd, "maxHeads", maxHeads); + assertEquals(maxHeads, cmd.getMaxHeads()); + } + + @Test + public void getMaxResolutionX() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertNull(cmd.getMaxResolutionX()); + Long maxResolutionX = 1920L; // Example resolution + ReflectionTestUtils.setField(cmd, "maxResolutionX", maxResolutionX); + assertEquals(maxResolutionX, cmd.getMaxResolutionX()); + } + + @Test + public void getMaxResolutionY() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertNull(cmd.getMaxResolutionY()); + Long maxResolutionY = 1080L; // Example resolution + ReflectionTestUtils.setField(cmd, "maxResolutionY", maxResolutionY); + assertEquals(maxResolutionY, cmd.getMaxResolutionY()); + } + + @Test + public void getEntityOwnerId() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCategoryCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCategoryCmdTest.java new file mode 100644 index 000000000000..32230056b6aa --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCategoryCmdTest.java @@ -0,0 +1,46 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.guest; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class AddGuestOsCategoryCmdTest { + + @Test + public void testGetArch() { + AddGuestOsCategoryCmd cmd = new AddGuestOsCategoryCmd(); + Assert.assertNull(cmd.getName()); + String name = "Name"; + ReflectionTestUtils.setField(cmd, "name", name); + Assert.assertEquals(name, cmd.getName()); + } + + @Test + public void testIsFeatured() { + AddGuestOsCategoryCmd cmd = new AddGuestOsCategoryCmd(); + Assert.assertFalse(cmd.isFeatured()); + ReflectionTestUtils.setField(cmd, "featured", false); + Assert.assertFalse(cmd.isFeatured()); + ReflectionTestUtils.setField(cmd, "featured", true); + Assert.assertTrue(cmd.isFeatured()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCategoryCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCategoryCmdTest.java new file mode 100644 index 000000000000..3f6a943b3fe9 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCategoryCmdTest.java @@ -0,0 +1,55 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.guest; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class UpdateGuestOsCategoryCmdTest { + + @Test + public void testGetArch() { + UpdateGuestOsCategoryCmd cmd = new UpdateGuestOsCategoryCmd(); + Assert.assertNull(cmd.getName()); + String name = "Name"; + ReflectionTestUtils.setField(cmd, "name", name); + Assert.assertEquals(name, cmd.getName()); + } + + @Test + public void testIsFeatured() { + UpdateGuestOsCategoryCmd cmd = new UpdateGuestOsCategoryCmd(); + Assert.assertNull(cmd.isFeatured()); + ReflectionTestUtils.setField(cmd, "featured", false); + Assert.assertFalse(cmd.isFeatured()); + ReflectionTestUtils.setField(cmd, "featured", true); + Assert.assertTrue(cmd.isFeatured()); + } + + @Test + public void testGetSortKey() { + UpdateGuestOsCategoryCmd cmd = new UpdateGuestOsCategoryCmd(); + Assert.assertNull(cmd.getSortKey()); + Integer sortKey = 100; + ReflectionTestUtils.setField(cmd, "sortKey", sortKey); + Assert.assertEquals(sortKey, cmd.getSortKey()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmdTest.java new file mode 100644 index 000000000000..f03a791776ed --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmdTest.java @@ -0,0 +1,37 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.guest; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class UpdateGuestOsCmdTest { + + + @Test + public void testGetZoneId() { + UpdateGuestOsCmd cmd = new UpdateGuestOsCmd(); + Assert.assertNull(cmd.getOsCategoryId()); + Long osCategoryId = 100L; + ReflectionTestUtils.setField(cmd, "osCategoryId", osCategoryId); + Assert.assertEquals(osCategoryId, cmd.getOsCategoryId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForGuestNetworkCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForGuestNetworkCmdTest.java new file mode 100644 index 000000000000..4039ca6dc948 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForGuestNetworkCmdTest.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.network; + +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; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.UUID; + +@RunWith(MockitoJUnitRunner.class) +public class CreateIpv4SubnetForGuestNetworkCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testCreateIpv4SubnetForGuestNetworkCmd() { + Long parentId = 1L; + UUID parentUuid = UUID.randomUUID(); + String subnet = "192.168.1.0/24"; + Integer cidrSize = 26; + + CreateIpv4SubnetForGuestNetworkCmd cmd = new CreateIpv4SubnetForGuestNetworkCmd(); + ReflectionTestUtils.setField(cmd, "parentId", parentId); + ReflectionTestUtils.setField(cmd, "subnet", subnet); + 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, parentUuid), cmd.getEventDescription()); + + Ipv4GuestSubnetNetworkMap ipv4GuestSubnetNetworkMap = Mockito.mock(Ipv4GuestSubnetNetworkMap.class); + Mockito.when(routedIpv4Manager.createIpv4SubnetForGuestNetwork(cmd)).thenReturn(ipv4GuestSubnetNetworkMap); + + Ipv4SubnetForGuestNetworkResponse response = Mockito.mock(Ipv4SubnetForGuestNetworkResponse.class); + Mockito.when(routedIpv4Manager.createIpv4SubnetForGuestNetworkResponse(ipv4GuestSubnetNetworkMap)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(response, cmd.getResponseObject()); + } +} 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 new file mode 100644 index 000000000000..bb324aca0e70 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/CreateIpv4SubnetForZoneCmdTest.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.network; + +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; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.UUID; + +@RunWith(MockitoJUnitRunner.class) +public class CreateIpv4SubnetForZoneCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testCreateIpv4SubnetForZoneCmd() { + Long zoneId = 1L; + UUID zoneUuid = UUID.randomUUID(); + String subnet = "192.168.1.0/24"; + String accountName = "user"; + Long projectId = 10L; + Long domainId = 11L; + + CreateIpv4SubnetForZoneCmd cmd = new CreateIpv4SubnetForZoneCmd(); + ReflectionTestUtils.setField(cmd, "zoneId", zoneId); + ReflectionTestUtils.setField(cmd, "subnet", subnet); + ReflectionTestUtils.setField(cmd, "accountName", accountName); + ReflectionTestUtils.setField(cmd,"projectId", projectId); + 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()); + Assert.assertEquals(projectId, cmd.getProjectId()); + 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, zoneUuid), cmd.getEventDescription()); + + DataCenterIpv4GuestSubnet zoneSubnet = Mockito.mock(DataCenterIpv4GuestSubnet.class); + Mockito.when(routedIpv4Manager.createDataCenterIpv4GuestSubnet(cmd)).thenReturn(zoneSubnet); + + DataCenterIpv4SubnetResponse response = Mockito.mock(DataCenterIpv4SubnetResponse.class); + Mockito.when(routedIpv4Manager.createDataCenterIpv4SubnetResponse(zoneSubnet)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(response, cmd.getResponseObject()); + } +} 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 new file mode 100644 index 000000000000..31458d2833fa --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DedicateIpv4SubnetForZoneCmdTest.java @@ -0,0 +1,78 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.network; + +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; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.UUID; + +@RunWith(MockitoJUnitRunner.class) +public class DedicateIpv4SubnetForZoneCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testDedicateIpv4SubnetForZoneCmd() { + Long id = 1L; + UUID uuid = UUID.randomUUID(); + String accountName = "user"; + Long projectId = 10L; + Long domainId = 11L; + + DedicateIpv4SubnetForZoneCmd cmd = new DedicateIpv4SubnetForZoneCmd(); + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd, "accountName", accountName); + ReflectionTestUtils.setField(cmd,"projectId", projectId); + 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()); + Assert.assertEquals(domainId, cmd.getDomainId()); + + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + Assert.assertEquals(EventTypes.EVENT_ZONE_IP4_SUBNET_DEDICATE, cmd.getEventType()); + 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); + + DataCenterIpv4SubnetResponse response = Mockito.mock(DataCenterIpv4SubnetResponse.class); + Mockito.when(routedIpv4Manager.createDataCenterIpv4SubnetResponse(zoneSubnet)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(response, cmd.getResponseObject()); + } +} 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 new file mode 100644 index 000000000000..48aceeaaeec1 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForGuestNetworkCmdTest.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.network; + +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; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.UUID; + +@RunWith(MockitoJUnitRunner.class) +public class DeleteIpv4SubnetForGuestNetworkCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testDeleteIpv4SubnetForGuestNetworkCmd() { + Long id = 1L; + UUID uuid = UUID.randomUUID(); + + 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 with ID: %s", uuid), cmd.getEventDescription()); + + Mockito.when(routedIpv4Manager.deleteIpv4SubnetForGuestNetwork(cmd)).thenReturn(true); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertTrue(cmd.getResponseObject() instanceof SuccessResponse); + } +} 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 new file mode 100644 index 000000000000..5c3593a8f1b6 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/DeleteIpv4SubnetForZoneCmdTest.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.network; + +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; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.UUID; + +@RunWith(MockitoJUnitRunner.class) +public class DeleteIpv4SubnetForZoneCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testDeleteIpv4SubnetForZoneCmd() { + Long id = 1L; + UUID uuid = UUID.randomUUID(); + + 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 with ID: %s", uuid), cmd.getEventDescription()); + + Mockito.when(routedIpv4Manager.deleteDataCenterIpv4GuestSubnet(cmd)).thenReturn(true); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertTrue(cmd.getResponseObject() instanceof SuccessResponse); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/ListIpv4SubnetsForGuestNetworkCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/ListIpv4SubnetsForGuestNetworkCmdTest.java new file mode 100644 index 000000000000..cbfe34f774ac --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/ListIpv4SubnetsForGuestNetworkCmdTest.java @@ -0,0 +1,83 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.network; + +import org.apache.cloudstack.api.response.Ipv4SubnetForGuestNetworkResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.Arrays; +import java.util.List; + +@RunWith(MockitoJUnitRunner.class) +public class ListIpv4SubnetsForGuestNetworkCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testListIpv4SubnetsForGuestNetworkCmd() { + Long id = 1L; + Long zoneId = 2L; + Long parentId = 2L; + String subnet = "192.168.1.0/24"; + Long networkId = 10L; + Long vpcId = 11L; + + ListIpv4SubnetsForGuestNetworkCmd cmd = new ListIpv4SubnetsForGuestNetworkCmd(); + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd, "zoneId", zoneId); + ReflectionTestUtils.setField(cmd, "subnet", subnet); + ReflectionTestUtils.setField(cmd, "parentId", parentId); + ReflectionTestUtils.setField(cmd,"networkId", networkId); + ReflectionTestUtils.setField(cmd,"vpcId", vpcId); + ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + + Assert.assertEquals(id, cmd.getId()); + Assert.assertEquals(zoneId, cmd.getZoneId()); + Assert.assertEquals(subnet, cmd.getSubnet()); + Assert.assertEquals(networkId, cmd.getNetworkId()); + Assert.assertEquals(vpcId, cmd.getVpcId()); + Assert.assertEquals(parentId, cmd.getParentId()); + + Assert.assertEquals(0L, cmd.getEntityOwnerId()); + + Ipv4GuestSubnetNetworkMap ipv4GuestSubnetNetworkMap = Mockito.mock(Ipv4GuestSubnetNetworkMap.class); + List ipv4GuestSubnetNetworkMaps = Arrays.asList(ipv4GuestSubnetNetworkMap); + Mockito.when(routedIpv4Manager.listIpv4GuestSubnetsForGuestNetwork(cmd)).thenReturn(ipv4GuestSubnetNetworkMaps); + + Ipv4SubnetForGuestNetworkResponse response = Mockito.mock(Ipv4SubnetForGuestNetworkResponse.class); + Mockito.when(routedIpv4Manager.createIpv4SubnetForGuestNetworkResponse(ipv4GuestSubnetNetworkMap)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertTrue(cmd.getResponseObject() instanceof ListResponse); + ListResponse listResponse = (ListResponse) cmd.getResponseObject(); + Assert.assertEquals(1, (int) listResponse.getCount()); + Assert.assertEquals(response, listResponse.getResponses().get(0)); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/ListIpv4SubnetsForZoneCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/ListIpv4SubnetsForZoneCmdTest.java new file mode 100644 index 000000000000..2c7a246f70f8 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/ListIpv4SubnetsForZoneCmdTest.java @@ -0,0 +1,83 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.network; + +import org.apache.cloudstack.api.response.DataCenterIpv4SubnetResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.Arrays; +import java.util.List; + +@RunWith(MockitoJUnitRunner.class) +public class ListIpv4SubnetsForZoneCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testListIpv4SubnetsForZoneCmd() { + Long id = 1L; + Long zoneId = 2L; + String subnet = "192.168.1.0/24"; + String accountName = "user"; + Long projectId = 10L; + Long domainId = 11L; + + ListIpv4SubnetsForZoneCmd cmd = new ListIpv4SubnetsForZoneCmd(); + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd, "zoneId", zoneId); + ReflectionTestUtils.setField(cmd, "subnet", subnet); + ReflectionTestUtils.setField(cmd, "accountName", accountName); + ReflectionTestUtils.setField(cmd,"projectId", projectId); + ReflectionTestUtils.setField(cmd,"domainId", domainId); + ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + + Assert.assertEquals(id, cmd.getId()); + Assert.assertEquals(zoneId, cmd.getZoneId()); + Assert.assertEquals(subnet, cmd.getSubnet()); + Assert.assertEquals(accountName, cmd.getAccountName()); + Assert.assertEquals(projectId, cmd.getProjectId()); + Assert.assertEquals(domainId, cmd.getDomainId()); + + Assert.assertEquals(0L, cmd.getEntityOwnerId()); + + DataCenterIpv4GuestSubnet zoneSubnet = Mockito.mock(DataCenterIpv4GuestSubnet.class); + List zoneSubnets = Arrays.asList(zoneSubnet); + Mockito.when(routedIpv4Manager.listDataCenterIpv4GuestSubnets(cmd)).thenReturn(zoneSubnets); + + DataCenterIpv4SubnetResponse response = Mockito.mock(DataCenterIpv4SubnetResponse.class); + Mockito.when(routedIpv4Manager.createDataCenterIpv4SubnetResponse(zoneSubnet)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertTrue(cmd.getResponseObject() instanceof ListResponse); + ListResponse listResponse = (ListResponse) cmd.getResponseObject(); + Assert.assertEquals(1, (int) listResponse.getCount()); + Assert.assertEquals(response, listResponse.getResponses().get(0)); + } +} 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 new file mode 100644 index 000000000000..29d6d8e735bb --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/ReleaseDedicatedIpv4SubnetForZoneCmdTest.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.network; + +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; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.UUID; + +@RunWith(MockitoJUnitRunner.class) +public class ReleaseDedicatedIpv4SubnetForZoneCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testReleaseDedicatedIpv4SubnetForZoneCmd() { + Long id = 1L; + UUID uuid = UUID.randomUUID(); + + 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 dedicated zone IPv4 subnet with ID: %s", uuid), cmd.getEventDescription()); + + DataCenterIpv4GuestSubnet zoneSubnet = Mockito.mock(DataCenterIpv4GuestSubnet.class); + Mockito.when(routedIpv4Manager.releaseDedicatedDataCenterIpv4GuestSubnet(cmd)).thenReturn(zoneSubnet); + + DataCenterIpv4SubnetResponse response = Mockito.mock(DataCenterIpv4SubnetResponse.class); + Mockito.when(routedIpv4Manager.createDataCenterIpv4SubnetResponse(zoneSubnet)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(response, cmd.getResponseObject()); + } +} 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 new file mode 100644 index 000000000000..399b77de6e85 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/UpdateIpv4SubnetForZoneCmdTest.java @@ -0,0 +1,72 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.network; + +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; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.UUID; + +@RunWith(MockitoJUnitRunner.class) +public class UpdateIpv4SubnetForZoneCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testUpdateIpv4SubnetForZoneCmd() { + Long id = 1L; + UUID uuid = UUID.randomUUID(); + String subnet = "192.168.1.0/24"; + + UpdateIpv4SubnetForZoneCmd cmd = new UpdateIpv4SubnetForZoneCmd(); + ReflectionTestUtils.setField(cmd, "id", id); + 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 with ID: %s", uuid), cmd.getEventDescription()); + + DataCenterIpv4GuestSubnet zoneSubnet = Mockito.mock(DataCenterIpv4GuestSubnet.class); + Mockito.when(routedIpv4Manager.updateDataCenterIpv4GuestSubnet(cmd)).thenReturn(zoneSubnet); + + DataCenterIpv4SubnetResponse response = Mockito.mock(DataCenterIpv4SubnetResponse.class); + Mockito.when(routedIpv4Manager.createDataCenterIpv4SubnetResponse(zoneSubnet)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(response, cmd.getResponseObject()); + } +} 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 new file mode 100644 index 000000000000..9cd403ddd1de --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForNetworkCmdTest.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.network.bgp; + +import com.cloud.event.EventTypes; + +import com.cloud.network.Network; +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; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +@RunWith(MockitoJUnitRunner.class) +public class ChangeBgpPeersForNetworkCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + ResponseGenerator _responseGenerator = Mockito.spy(ResponseGenerator.class); + + @Test + public void testChangeBgpPeersForNetworkCmd() { + Long networkId = 10L; + UUID networkUuid = UUID.randomUUID(); + List bgpPeerIds = Arrays.asList(20L, 21L); + + ChangeBgpPeersForNetworkCmd cmd = new ChangeBgpPeersForNetworkCmd(); + ReflectionTestUtils.setField(cmd, "networkId", networkId); + ReflectionTestUtils.setField(cmd, "bgpPeerIds", bgpPeerIds); + 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 with ID: %s", networkUuid), cmd.getEventDescription()); + + Network network = Mockito.mock(Network.class); + Mockito.when(routedIpv4Manager.changeBgpPeersForNetwork(cmd)).thenReturn(network); + + NetworkResponse response = Mockito.mock(NetworkResponse.class); + Mockito.when(_responseGenerator.createNetworkResponse(ResponseObject.ResponseView.Full, network)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(response, cmd.getResponseObject()); + } +} 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 new file mode 100644 index 000000000000..545523e3ab97 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ChangeBgpPeersForVpcCmdTest.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.network.bgp; + +import com.cloud.event.EventTypes; + +import com.cloud.network.vpc.Vpc; +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; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +@RunWith(MockitoJUnitRunner.class) +public class ChangeBgpPeersForVpcCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + ResponseGenerator _responseGenerator = Mockito.spy(ResponseGenerator.class); + + @Test + public void testChangeBgpPeersForVpcCmd() { + Long VpcId = 10L; + UUID vpcUuid = UUID.randomUUID(); + List bgpPeerIds = Arrays.asList(20L, 21L); + + ChangeBgpPeersForVpcCmd cmd = new ChangeBgpPeersForVpcCmd(); + ReflectionTestUtils.setField(cmd, "vpcId", VpcId); + ReflectionTestUtils.setField(cmd, "bgpPeerIds", bgpPeerIds); + 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 with ID: %s", vpcUuid), cmd.getEventDescription()); + + Vpc Vpc = Mockito.mock(Vpc.class); + Mockito.when(routedIpv4Manager.changeBgpPeersForVpc(cmd)).thenReturn(Vpc); + + VpcResponse response = Mockito.mock(VpcResponse.class); + Mockito.when(_responseGenerator.createVpcResponse(ResponseObject.ResponseView.Full, Vpc)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(response, cmd.getResponseObject()); + } +} 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 new file mode 100644 index 000000000000..866824f62936 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/CreateBgpPeerCmdTest.java @@ -0,0 +1,91 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.network.bgp; + +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; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.UUID; + +@RunWith(MockitoJUnitRunner.class) +public class CreateBgpPeerCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testCreateBgpPeerCmd() { + Long zoneId = 1L; + UUID zoneUuid = UUID.randomUUID(); + String accountName = "user"; + Long projectId = 10L; + Long domainId = 11L; + String ip4Address = "ip4-address"; + String ip6Address = "ip6-address"; + Long peerAsNumber = 15000L; + String peerPassword = "peer-password"; + + CreateBgpPeerCmd cmd = new CreateBgpPeerCmd(); + ReflectionTestUtils.setField(cmd, "zoneId", zoneId); + ReflectionTestUtils.setField(cmd, "ip4Address", ip4Address); + ReflectionTestUtils.setField(cmd, "ip6Address", ip6Address); + ReflectionTestUtils.setField(cmd, "asNumber", peerAsNumber); + ReflectionTestUtils.setField(cmd, "password", peerPassword); + ReflectionTestUtils.setField(cmd, "accountName", accountName); + ReflectionTestUtils.setField(cmd,"projectId", projectId); + 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()); + Assert.assertEquals(peerAsNumber, cmd.getAsNumber()); + Assert.assertEquals(peerPassword, cmd.getPassword()); + Assert.assertEquals(accountName, cmd.getAccountName()); + Assert.assertEquals(projectId, cmd.getProjectId()); + Assert.assertEquals(domainId, cmd.getDomainId()); + + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + Assert.assertEquals(EventTypes.EVENT_BGP_PEER_CREATE, cmd.getEventType()); + 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); + + BgpPeerResponse response = Mockito.mock(BgpPeerResponse.class); + Mockito.when(routedIpv4Manager.createBgpPeerResponse(bgpPeer)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(response, cmd.getResponseObject()); + } +} 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 new file mode 100644 index 000000000000..a8046d3d745c --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/DedicateBgpPeerCmdTest.java @@ -0,0 +1,78 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.network.bgp; + +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; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.UUID; + +@RunWith(MockitoJUnitRunner.class) +public class DedicateBgpPeerCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testDedicateBgpPeerCmd() { + Long id = 1L; + UUID uuid = UUID.randomUUID(); + String accountName = "user"; + Long projectId = 10L; + Long domainId = 11L; + + DedicateBgpPeerCmd cmd = new DedicateBgpPeerCmd(); + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd, "accountName", accountName); + ReflectionTestUtils.setField(cmd,"projectId", projectId); + 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()); + Assert.assertEquals(domainId, cmd.getDomainId()); + + Assert.assertEquals(1L, cmd.getEntityOwnerId()); + Assert.assertEquals(EventTypes.EVENT_BGP_PEER_DEDICATE, cmd.getEventType()); + 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); + + BgpPeerResponse response = Mockito.mock(BgpPeerResponse.class); + Mockito.when(routedIpv4Manager.createBgpPeerResponse(bgpPeer)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(response, cmd.getResponseObject()); + } +} 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 new file mode 100644 index 000000000000..d54be9e859e4 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/DeleteBgpPeerCmdTest.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.network.bgp; + +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; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.UUID; + +@RunWith(MockitoJUnitRunner.class) +public class DeleteBgpPeerCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testDeleteBgpPeerCmd() { + Long id = 1L; + UUID uuid = UUID.randomUUID(); + + 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 with ID: %s", uuid), cmd.getEventDescription()); + + Mockito.when(routedIpv4Manager.deleteBgpPeer(cmd)).thenReturn(true); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertTrue(cmd.getResponseObject() instanceof SuccessResponse); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ListBgpPeersCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ListBgpPeersCmdTest.java new file mode 100644 index 000000000000..cb2027951adc --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ListBgpPeersCmdTest.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.admin.network.bgp; + +import org.apache.cloudstack.api.response.BgpPeerResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.network.BgpPeer; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.Arrays; +import java.util.List; + +@RunWith(MockitoJUnitRunner.class) +public class ListBgpPeersCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testIsDedicated() { + ListBgpPeersCmd cmd = new ListBgpPeersCmd(); + + Assert.assertNull(cmd.getDedicated()); + + ReflectionTestUtils.setField(cmd, "isDedicated", Boolean.TRUE); + Assert.assertTrue(cmd.getDedicated()); + + ReflectionTestUtils.setField(cmd, "isDedicated", Boolean.FALSE); + Assert.assertFalse(cmd.getDedicated()); + } + + @Test + public void testListBgpPeersCmd() { + Long id = 1L; + Long zoneId = 2L; + Long peerAsNumber = 15000L; + String accountName = "user"; + Long projectId = 10L; + Long domainId = 11L; + + ListBgpPeersCmd cmd = new ListBgpPeersCmd(); + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd, "zoneId", zoneId); + ReflectionTestUtils.setField(cmd, "asNumber", peerAsNumber); + ReflectionTestUtils.setField(cmd, "accountName", accountName); + ReflectionTestUtils.setField(cmd,"projectId", projectId); + ReflectionTestUtils.setField(cmd,"domainId", domainId); + ReflectionTestUtils.setField(cmd,"routedIpv4Manager", routedIpv4Manager); + + Assert.assertEquals(id, cmd.getId()); + Assert.assertEquals(zoneId, cmd.getZoneId()); + Assert.assertEquals(peerAsNumber, cmd.getAsNumber()); + Assert.assertEquals(accountName, cmd.getAccountName()); + Assert.assertEquals(projectId, cmd.getProjectId()); + Assert.assertEquals(domainId, cmd.getDomainId()); + + Assert.assertEquals(0L, cmd.getEntityOwnerId()); + + BgpPeer bgpPeer = Mockito.mock(BgpPeer.class); + List bgpPeers = Arrays.asList(bgpPeer); + Mockito.when(routedIpv4Manager.listBgpPeers(cmd)).thenReturn(bgpPeers); + + BgpPeerResponse response = Mockito.mock(BgpPeerResponse.class); + Mockito.when(routedIpv4Manager.createBgpPeerResponse(bgpPeer)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertTrue(cmd.getResponseObject() instanceof ListResponse); + ListResponse listResponse = (ListResponse) cmd.getResponseObject(); + Assert.assertEquals(1, (int) listResponse.getCount()); + Assert.assertEquals(response, listResponse.getResponses().get(0)); + } +} 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 new file mode 100644 index 000000000000..1cf8ead706d1 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/ReleaseDedicatedBgpPeerCmdTest.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.network.bgp; + +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; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.UUID; + +@RunWith(MockitoJUnitRunner.class) +public class ReleaseDedicatedBgpPeerCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testReleaseDedicatedBgpPeerCmd() { + Long id = 1L; + UUID uuid = UUID.randomUUID(); + + 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 dedicated BGP Peer with ID: %s", uuid), cmd.getEventDescription()); + + BgpPeer bgpPeer = Mockito.mock(BgpPeer.class); + Mockito.when(routedIpv4Manager.releaseDedicatedBgpPeer(cmd)).thenReturn(bgpPeer); + + BgpPeerResponse response = Mockito.mock(BgpPeerResponse.class); + Mockito.when(routedIpv4Manager.createBgpPeerResponse(bgpPeer)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(response, cmd.getResponseObject()); + } +} 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 new file mode 100644 index 000000000000..1601fcb4c5a5 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/network/bgp/UpdateBgpPeerCmdTest.java @@ -0,0 +1,93 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.network.bgp; + +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; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.UUID; + +@RunWith(MockitoJUnitRunner.class) +public class UpdateBgpPeerCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + @Test + public void testUpdateBgpPeerCmd() { + Long id = 1L; + UUID uuid = UUID.randomUUID(); + String ip4Address = "ip4-address"; + String ip6Address = "ip6-address"; + Long peerAsNumber = 15000L; + String peerPassword = "peer-password"; + + UpdateBgpPeerCmd cmd = new UpdateBgpPeerCmd(); + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd, "ip4Address", ip4Address); + ReflectionTestUtils.setField(cmd, "ip6Address", ip6Address); + ReflectionTestUtils.setField(cmd, "asNumber", peerAsNumber); + 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()); + Assert.assertEquals(peerAsNumber, cmd.getAsNumber()); + 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 with ID: %s", uuid), cmd.getEventDescription()); + + BgpPeer bgpPeer = Mockito.mock(BgpPeer.class); + Mockito.when(routedIpv4Manager.updateBgpPeer(cmd)).thenReturn(bgpPeer); + + BgpPeerResponse response = Mockito.mock(BgpPeerResponse.class); + Mockito.when(routedIpv4Manager.createBgpPeerResponse(bgpPeer)).thenReturn(response); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(response, cmd.getResponseObject()); + } + + @Test + public void testUpdateBgpPeerCleanupDetails() { + UpdateBgpPeerCmd cmd = new UpdateBgpPeerCmd(); + Assert.assertFalse(cmd.isCleanupDetails()); + + ReflectionTestUtils.setField(cmd, "cleanupDetails", Boolean.TRUE); + Assert.assertTrue(cmd.isCleanupDetails()); + + ReflectionTestUtils.setField(cmd, "cleanupDetails", Boolean.FALSE); + Assert.assertFalse(cmd.isCleanupDetails()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateNetworkOfferingCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateNetworkOfferingCmdTest.java index 8b95456a84c2..ef10ebff4678 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateNetworkOfferingCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateNetworkOfferingCmdTest.java @@ -23,14 +23,16 @@ import org.mockito.InjectMocks; import org.springframework.test.util.ReflectionTestUtils; + public class CreateNetworkOfferingCmdTest { @InjectMocks private CreateNetworkOfferingCmd createNetworkOfferingCmd = new CreateNetworkOfferingCmd(); + String netName = "network"; + @Test public void createVpcNtwkOffWithEmptyDisplayText() { - String netName = "network"; ReflectionTestUtils.setField(createNetworkOfferingCmd, "networkOfferingName", netName); Assert.assertEquals(createNetworkOfferingCmd.getDisplayText(), netName); } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmdTest.java index f69e8cea4f3d..bc7f65b07561 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmdTest.java @@ -17,6 +17,8 @@ package org.apache.cloudstack.api.command.admin.offering; +import com.cloud.exception.InvalidParameterValueException; +import org.apache.cloudstack.vm.lease.VMLeaseManager; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -37,4 +39,62 @@ public void testGetDisplayTextWhenEmpty() { Assert.assertEquals(createServiceOfferingCmd.getDisplayText(), netName); } + @Test + public void testIsPurgeResourcesNoOrNullValue() { + Assert.assertFalse(createServiceOfferingCmd.isPurgeResources()); + ReflectionTestUtils.setField(createServiceOfferingCmd, "purgeResources", false); + Assert.assertFalse(createServiceOfferingCmd.isPurgeResources()); + } + + @Test + public void testIsPurgeResourcesFalse() { + ReflectionTestUtils.setField(createServiceOfferingCmd, "purgeResources", false); + Assert.assertFalse(createServiceOfferingCmd.isPurgeResources()); + } + + @Test + public void testIsPurgeResourcesTrue() { + ReflectionTestUtils.setField(createServiceOfferingCmd, "purgeResources", true); + Assert.assertTrue(createServiceOfferingCmd.isPurgeResources()); + } + + @Test + public void testGetLeaseDuration() { + ReflectionTestUtils.setField(createServiceOfferingCmd, "leaseDuration", 10); + Assert.assertEquals(10, createServiceOfferingCmd.getLeaseDuration().longValue()); + } + + @Test + public void testGetLeaseExpiryAction() { + ReflectionTestUtils.setField(createServiceOfferingCmd, "leaseExpiryAction", "stop"); + Assert.assertEquals(VMLeaseManager.ExpiryAction.STOP, createServiceOfferingCmd.getLeaseExpiryAction()); + + ReflectionTestUtils.setField(createServiceOfferingCmd, "leaseExpiryAction", "DESTROY"); + Assert.assertEquals(VMLeaseManager.ExpiryAction.DESTROY, createServiceOfferingCmd.getLeaseExpiryAction()); + } + + @Test(expected = InvalidParameterValueException.class) + public void testGetLeaseExpiryActionInvalidValue() { + ReflectionTestUtils.setField(createServiceOfferingCmd, "leaseExpiryAction", "Unknown"); + Assert.assertEquals(null, createServiceOfferingCmd.getLeaseExpiryAction()); + } + + @Test + public void testGetVgpuProfileId() { + ReflectionTestUtils.setField(createServiceOfferingCmd, "vgpuProfileId", 10L); + Assert.assertEquals(10L, createServiceOfferingCmd.getVgpuProfileId().longValue()); + } + + @Test + public void testGetGpuCount() { + ReflectionTestUtils.setField(createServiceOfferingCmd, "gpuCount", 2); + Assert.assertEquals(2, createServiceOfferingCmd.getGpuCount().intValue()); + } + + @Test + public void testGetGpuDisplay() { + ReflectionTestUtils.setField(createServiceOfferingCmd, "gpuDisplay", true); + Assert.assertTrue(createServiceOfferingCmd.getGpuDisplay()); + } + } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/UpdateServiceOfferingCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/UpdateServiceOfferingCmdTest.java new file mode 100644 index 000000000000..1bb2be041e18 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/UpdateServiceOfferingCmdTest.java @@ -0,0 +1,51 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.offering; + +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 UpdateServiceOfferingCmdTest { + + @InjectMocks + private UpdateServiceOfferingCmd updateServiceOfferingCmd; + + @Test + public void testIsPurgeResourcesNoOrNullValue() { + Assert.assertFalse(updateServiceOfferingCmd.isPurgeResources()); + ReflectionTestUtils.setField(updateServiceOfferingCmd, "purgeResources", false); + Assert.assertFalse(updateServiceOfferingCmd.isPurgeResources()); + } + + @Test + public void testIsPurgeResourcesFalse() { + ReflectionTestUtils.setField(updateServiceOfferingCmd, "purgeResources", false); + Assert.assertFalse(updateServiceOfferingCmd.isPurgeResources()); + } + + @Test + public void testIsPurgeResourcesTrue() { + ReflectionTestUtils.setField(updateServiceOfferingCmd, "purgeResources", true); + Assert.assertTrue(updateServiceOfferingCmd.isPurgeResources()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/resource/ListCapacityCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/resource/ListCapacityCmdTest.java new file mode 100644 index 000000000000..fc0e6face529 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/resource/ListCapacityCmdTest.java @@ -0,0 +1,34 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.resource; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +public class ListCapacityCmdTest { + + @Test + public void testGetTag() { + ListCapacityCmd cmd = new ListCapacityCmd(); + ReflectionTestUtils.setField(cmd, "tag", null); + Assert.assertNull(cmd.getTag()); + String tag = "ABC"; + ReflectionTestUtils.setField(cmd, "tag", tag); + Assert.assertEquals(tag, cmd.getTag()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/resource/PurgeExpungedResourcesCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/resource/PurgeExpungedResourcesCmdTest.java new file mode 100644 index 000000000000..c8ca1c2afe30 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/resource/PurgeExpungedResourcesCmdTest.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.admin.resource; + +import static org.junit.Assert.assertNull; + +import java.util.Date; + +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.PurgeExpungedResourcesResponse; +import org.apache.cloudstack.resource.ResourceCleanupService; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; +import org.springframework.test.util.ReflectionTestUtils; + +import com.cloud.utils.exception.CloudRuntimeException; + +@RunWith(MockitoJUnitRunner.class) +public class PurgeExpungedResourcesCmdTest { + @Mock + ResourceCleanupService resourceCleanupService; + + @Spy + @InjectMocks + PurgeExpungedResourcesCmd spyCmd; + + @Test + public void testGetResourceType() { + PurgeExpungedResourcesCmd cmd = new PurgeExpungedResourcesCmd(); + assertNull(cmd.getResourceType()); + ReflectionTestUtils.setField(cmd, "resourceType", ResourceCleanupService.ResourceType.VirtualMachine.toString()); + Assert.assertEquals(ResourceCleanupService.ResourceType.VirtualMachine.toString(), cmd.getResourceType()); + } + + @Test + public void testGetBatchSize() { + PurgeExpungedResourcesCmd cmd = new PurgeExpungedResourcesCmd(); + assertNull(cmd.getBatchSize()); + Long batchSize = 100L; + ReflectionTestUtils.setField(cmd, "batchSize", batchSize); + Assert.assertEquals(batchSize, cmd.getBatchSize()); + } + + @Test + public void testGetStartDate() { + PurgeExpungedResourcesCmd cmd = new PurgeExpungedResourcesCmd(); + assertNull(cmd.getStartDate()); + Date date = new Date(); + ReflectionTestUtils.setField(cmd, "startDate", date); + Assert.assertEquals(date, cmd.getStartDate()); + } + + @Test + public void testGetEndDate() { + PurgeExpungedResourcesCmd cmd = new PurgeExpungedResourcesCmd(); + assertNull(cmd.getEndDate()); + Date date = new Date(); + ReflectionTestUtils.setField(cmd, "endDate", date); + Assert.assertEquals(date, cmd.getEndDate()); + } + + @Test + public void testExecute() { + final PurgeExpungedResourcesResponse[] executeResponse = new PurgeExpungedResourcesResponse[1]; + Long result = 100L; + Mockito.when(resourceCleanupService.purgeExpungedResources(Mockito.any())).thenReturn(result); + Mockito.doAnswer((Answer) invocation -> { + executeResponse[0] = (PurgeExpungedResourcesResponse)invocation.getArguments()[0]; + return null; + }).when(spyCmd).setResponseObject(Mockito.any()); + spyCmd.execute(); + PurgeExpungedResourcesResponse response = executeResponse[0]; + Assert.assertNotNull(response); + Assert.assertEquals(result, response.getResourceCount()); + } + + @Test(expected = ServerApiException.class) + public void testExecuteException() { + Mockito.doThrow(CloudRuntimeException.class).when(resourceCleanupService).purgeExpungedResources(Mockito.any()); + spyCmd.execute(); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/storage/AddObjectStoragePoolCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/storage/AddObjectStoragePoolCmdTest.java index a69a7a858ce0..c7aeb8ba99bf 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/storage/AddObjectStoragePoolCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/storage/AddObjectStoragePoolCmdTest.java @@ -61,6 +61,8 @@ public class AddObjectStoragePoolCmdTest { String provider = "Simulator"; + Long size = 10L; + Map details; private AutoCloseable closeable; @@ -74,6 +76,7 @@ public void setUp() throws Exception { ReflectionTestUtils.setField(addObjectStoragePoolCmdSpy, "url", url); ReflectionTestUtils.setField(addObjectStoragePoolCmdSpy, "providerName", provider); ReflectionTestUtils.setField(addObjectStoragePoolCmdSpy, "details", details); + ReflectionTestUtils.setField(addObjectStoragePoolCmdSpy, "size", size); addObjectStoragePoolCmdSpy._storageService = storageService; addObjectStoragePoolCmdSpy._responseGenerator = responseGenerator; } @@ -87,12 +90,12 @@ public void tearDown() throws Exception { @Test public void testAddObjectStore() throws DiscoveryException { Mockito.doReturn(objectStore).when(storageService).discoverObjectStore(Mockito.anyString(), - Mockito.anyString(), Mockito.anyString(), any()); + Mockito.anyString(), Mockito.anyLong(), Mockito.anyString(), any()); ObjectStoreResponse objectStoreResponse = new ObjectStoreResponse(); Mockito.doReturn(objectStoreResponse).when(responseGenerator).createObjectStoreResponse(any()); addObjectStoragePoolCmdSpy.execute(); Mockito.verify(storageService, Mockito.times(1)) - .discoverObjectStore(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); + .discoverObjectStore(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); } } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/storage/DownloadImageStoreObjectCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/storage/DownloadImageStoreObjectCmdTest.java index 98435bf6e38a..3b2b49cae4e1 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,20 +19,19 @@ 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.After; import org.junit.Assert; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.test.util.ReflectionTestUtils; import java.util.List; +import java.util.UUID; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -48,18 +47,6 @@ public class DownloadImageStoreObjectCmdTest { @Spy private DownloadImageStoreObjectCmd cmd; - private AutoCloseable closeable; - - @Before - public void setUp() { - closeable = MockitoAnnotations.openMocks(this); - } - - @After - public void tearDown() throws Exception { - closeable.close(); - } - @Test public void testExecute() throws Exception { ReflectionTestUtils.setField(cmd, "storeId", 1L); @@ -110,10 +97,13 @@ public void testGetEventType() { @Test public void testGetEventDescription() { + UUID uuid = UUID.randomUUID(); + 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/MigrateVirtualMachineWithVolumeCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmdTest.java index 61a3c8fb9e65..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 @@ -68,6 +68,12 @@ public class MigrateVirtualMachineWithVolumeCmdTest { @Mock Host hostMock; + @Mock + private Object job; + + @Mock + private Object _responseObject; + @Spy @InjectMocks MigrateVirtualMachineWithVolumeCmd cmdSpy; @@ -97,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()); } } @@ -155,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()); } } @@ -181,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()); } } @@ -199,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 new file mode 100644 index 000000000000..235acb15eeab --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/volume/ImportVolumeCmdTest.java @@ -0,0 +1,83 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.volume; + +import com.cloud.event.EventTypes; +import com.cloud.user.AccountService; +import org.apache.cloudstack.api.response.VolumeResponse; +import org.apache.cloudstack.storage.volume.VolumeImportUnmanageService; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class ImportVolumeCmdTest { + + VolumeImportUnmanageService volumeImportService = Mockito.spy(VolumeImportUnmanageService.class); + AccountService accountService = Mockito.spy(AccountService.class); + + @Test + public void testImportVolumeCmd() { + String name = "volume name"; + String path = "file path"; + Long storageId = 2L; + Long diskOfferingId = 3L; + String accountName = "account"; + Long domainId = 4L; + Long projectId = 5L; + long accountId = 6L; + + Mockito.when(accountService.finalizeAccountId(accountName, domainId, projectId, true)).thenReturn(accountId); + + ImportVolumeCmd cmd = new ImportVolumeCmd(); + ReflectionTestUtils.setField(cmd, "path", path); + ReflectionTestUtils.setField(cmd, "name", name); + ReflectionTestUtils.setField(cmd, "storageId", storageId); + ReflectionTestUtils.setField(cmd, "diskOfferingId", diskOfferingId); + ReflectionTestUtils.setField(cmd, "accountName", accountName); + ReflectionTestUtils.setField(cmd, "domainId", domainId); + ReflectionTestUtils.setField(cmd, "projectId", projectId); + ReflectionTestUtils.setField(cmd,"volumeImportService", volumeImportService); + ReflectionTestUtils.setField(cmd, "_accountService", accountService); + + Assert.assertEquals(path, cmd.getPath()); + Assert.assertEquals(name, cmd.getName()); + Assert.assertEquals(storageId, cmd.getStorageId()); + Assert.assertEquals(diskOfferingId, cmd.getDiskOfferingId()); + Assert.assertEquals(accountName, cmd.getAccountName()); + Assert.assertEquals(domainId, cmd.getDomainId()); + Assert.assertEquals(projectId, cmd.getProjectId()); + + Assert.assertEquals(EventTypes.EVENT_VOLUME_IMPORT, cmd.getEventType()); + Assert.assertEquals("Importing unmanaged Volume with path: " + path, cmd.getEventDescription()); + Assert.assertEquals(accountId, cmd.getEntityOwnerId()); + + VolumeResponse response = Mockito.mock(VolumeResponse.class); + Mockito.when(volumeImportService.importVolume(cmd)).thenReturn(response); + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(response, cmd.getResponseObject()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/volume/ListVolumesForImportCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/volume/ListVolumesForImportCmdTest.java new file mode 100644 index 000000000000..959940d2a91d --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/volume/ListVolumesForImportCmdTest.java @@ -0,0 +1,58 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.volume; + +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.VolumeForImportResponse; +import org.apache.cloudstack.storage.volume.VolumeImportUnmanageService; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class ListVolumesForImportCmdTest { + + VolumeImportUnmanageService volumeImportService = Mockito.spy(VolumeImportUnmanageService.class); + + @Test + public void testListVolumesForImportCmd() { + Long storageId = 2L; + String filePath = "file path"; + + ListVolumesForImportCmd cmd = new ListVolumesForImportCmd(); + ReflectionTestUtils.setField(cmd, "storageId", storageId); + ReflectionTestUtils.setField(cmd, "path", filePath); + ReflectionTestUtils.setField(cmd,"volumeImportService", volumeImportService); + + Assert.assertEquals(storageId, cmd.getStorageId()); + Assert.assertEquals(filePath, cmd.getPath()); + + ListResponse response = Mockito.mock(ListResponse.class); + Mockito.when(volumeImportService.listVolumesForImport(cmd)).thenReturn(response); + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(response, cmd.getResponseObject()); + } +} 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 new file mode 100644 index 000000000000..ecca507a6b95 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/volume/UnmanageVolumeCmdTest.java @@ -0,0 +1,76 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.volume; + +import com.cloud.event.EventTypes; +import com.cloud.storage.Volume; +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; +import org.junit.runner.RunWith; + +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.UUID; + +@RunWith(MockitoJUnitRunner.class) +public class UnmanageVolumeCmdTest { + + VolumeImportUnmanageService volumeImportService = Mockito.spy(VolumeImportUnmanageService.class); + ResponseGenerator responseGenerator = Mockito.spy(ResponseGenerator.class); + + @Test + public void testUnmanageVolumeCmd() { + long accountId = 2L; + Long volumeId = 3L; + UUID volumeUuid = UUID.randomUUID(); + Volume volume = Mockito.mock(Volume.class); + + Mockito.when(responseGenerator.findVolumeById(volumeId)).thenReturn(volume); + Mockito.when(volume.getAccountId()).thenReturn(accountId); + + UnmanageVolumeCmd cmd = new UnmanageVolumeCmd(); + ReflectionTestUtils.setField(cmd, "volumeId", volumeId); + 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: " + volumeUuid, cmd.getEventDescription()); + + Mockito.when(volumeImportService.unmanageVolume(volumeId)).thenReturn(true); + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Object response = cmd.getResponseObject(); + Assert.assertTrue(response instanceof SuccessResponse); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCCmdByAdminTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCCmdByAdminTest.java new file mode 100644 index 000000000000..aaa65e10ff4e --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCCmdByAdminTest.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.admin.vpc; + +import com.cloud.network.vpc.VpcService; +import com.cloud.user.AccountService; +import com.cloud.utils.db.EntityManager; +import org.apache.cloudstack.api.ResponseGenerator; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.List; + +@RunWith(MockitoJUnitRunner.class) +public class CreateVPCCmdByAdminTest { + + @Mock + public VpcService _vpcService; + @Mock + public EntityManager _entityMgr; + @Mock + public AccountService _accountService; + private ResponseGenerator responseGenerator; + @Mock + public Object job; + @InjectMocks + CreateVPCCmdByAdmin cmd; + + @Test + public void testBgpPeerIds() { + List bgpPeerIds = Mockito.mock(List.class); + ReflectionTestUtils.setField(cmd, "bgpPeerIds", bgpPeerIds); + Assert.assertEquals(bgpPeerIds, cmd.getBgpPeerIds()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmdTest.java index 16b716d7d638..290a2850c9a6 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmdTest.java @@ -52,15 +52,15 @@ public void testServiceProvidersEmpty() throws IllegalArgumentException, IllegalAccessException { CreateVPCOfferingCmd cmd = new CreateVPCOfferingCmd(); ApiCmdTestUtil.set(cmd, ApiConstants.SERVICE_PROVIDER_LIST, new HashMap>()); - Assert.assertNull(cmd.getServiceProviders()); + Assert.assertTrue(cmd.getServiceProviders().isEmpty()); } @Test - public void getDetailsNull() throws IllegalArgumentException, + public void getDetailsEmpty() throws IllegalArgumentException, IllegalAccessException { CreateVPCOfferingCmd cmd = new CreateVPCOfferingCmd(); ApiCmdTestUtil.set(cmd, ApiConstants.SERVICE_PROVIDER_LIST, null); - Assert.assertNull(cmd.getServiceProviders()); + Assert.assertTrue(cmd.getServiceProviders().isEmpty()); } @Test 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/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/CreateRoleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/test/CreateRoleCmdTest.java index 4b9d4fd8974f..72ce95933647 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/test/CreateRoleCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/test/CreateRoleCmdTest.java @@ -54,6 +54,7 @@ public void testCreateRoleWithRoleType() { when(role.getDescription()).thenReturn("User test"); when(role.getName()).thenReturn("testuser"); when(role.getRoleType()).thenReturn(RoleType.User); + when(role.getState()).thenReturn(Role.State.ENABLED); when(roleService.createRole(createRoleCmd.getRoleName(), createRoleCmd.getRoleType(), createRoleCmd.getRoleDescription(), true)).thenReturn(role); createRoleCmd.execute(); RoleResponse response = (RoleResponse) createRoleCmd.getResponseObject(); @@ -71,6 +72,7 @@ public void testCreateRoleWithExistingRole() { when(newRole.getDescription()).thenReturn("User test"); when(newRole.getName()).thenReturn("testuser"); when(newRole.getRoleType()).thenReturn(RoleType.User); + when(newRole.getState()).thenReturn(Role.State.ENABLED); when(roleService.createRole(createRoleCmd.getRoleName(), role, createRoleCmd.getRoleDescription(), true)).thenReturn(newRole); createRoleCmd.execute(); RoleResponse response = (RoleResponse) createRoleCmd.getResponseObject(); 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 34baebe52574..b70efaf9a6c5 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"; - } }; } @@ -93,7 +90,7 @@ public void testCreateSuccess() { Snapshot snapshot = Mockito.mock(Snapshot.class); try { Mockito.when(volumeApiService.takeSnapshot(nullable(Long.class), nullable(Long.class), isNull(), - nullable(Account.class), nullable(Boolean.class), nullable(Snapshot.LocationType.class), nullable(Boolean.class), nullable(Map.class), nullable(List.class))).thenReturn(snapshot); + nullable(Account.class), nullable(Boolean.class), nullable(Snapshot.LocationType.class), nullable(Boolean.class), nullable(Map.class), nullable(List.class), nullable(List.class), Mockito.anyBoolean())).thenReturn(snapshot); } catch (Exception e) { Assert.fail("Received exception when success expected " + e.getMessage()); @@ -121,12 +118,15 @@ public void testCreateFailure() { AccountService accountService = Mockito.mock(AccountService.class); Account account = Mockito.mock(Account.class); Mockito.when(accountService.getAccount(anyLong())).thenReturn(account); + UUID volumeUuid = UUID.randomUUID(); + + CallContext.current().putApiResourceUuid("volumeid", volumeUuid); VolumeApiService volumeApiService = Mockito.mock(VolumeApiService.class); try { Mockito.when(volumeApiService.takeSnapshot(nullable(Long.class), nullable(Long.class), nullable(Long.class), - nullable(Account.class), nullable(Boolean.class), nullable(Snapshot.LocationType.class), nullable(Boolean.class), any(), Mockito.anyList())).thenReturn(null); + nullable(Account.class), nullable(Boolean.class), nullable(Snapshot.LocationType.class), nullable(Boolean.class), any(), Mockito.anyList(), Mockito.anyList(), Mockito.anyBoolean())).thenReturn(null); } catch (Exception e) { Assert.fail("Received exception when success expected " + e.getMessage()); } @@ -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/ImportRoleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/test/ImportRoleCmdTest.java index 6299c1ed8e2c..d2597e5162fd 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/test/ImportRoleCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/test/ImportRoleCmdTest.java @@ -32,19 +32,13 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyCollection; -import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import com.cloud.exception.InvalidParameterValueException; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.regex.Matcher; public class ImportRoleCmdTest { private ImportRoleCmd importRoleCmd; @@ -93,6 +87,7 @@ public void testImportRoleSuccess() { when(role.getDescription()).thenReturn("test user imported"); when(role.getName()).thenReturn("Test User"); when(role.getRoleType()).thenReturn(RoleType.User); + when(role.getState()).thenReturn(Role.State.ENABLED); when(roleService.importRole(anyString(), any(), anyString(), any(), anyBoolean(), anyBoolean())).thenReturn(role); importRoleCmd.execute(); 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..3dfb29dadd3e 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 UUID conditionUuid = UUID.randomUUID(); @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/UpdateRoleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/test/UpdateRoleCmdTest.java index 84b91525742a..9a1dae9a480a 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/test/UpdateRoleCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/test/UpdateRoleCmdTest.java @@ -62,6 +62,7 @@ public void testUpdateSuccess() { when(role.getId()).thenReturn(1L); when(role.getDescription()).thenReturn("Description Initial"); when(role.getName()).thenReturn("User"); + when(role.getState()).thenReturn(Role.State.ENABLED); updateRoleCmd.execute(); RoleResponse response = (RoleResponse) updateRoleCmd.getResponseObject(); assertEquals((String)ReflectionTestUtils.getField(response, "roleName"),role.getName()); 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/account/ListAccountsCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/account/ListAccountsCmdTest.java new file mode 100644 index 000000000000..a1ba9270345c --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/account/ListAccountsCmdTest.java @@ -0,0 +1,87 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.account; + +import java.util.List; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.response.AccountResponse; +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; + +import com.cloud.user.ResourceLimitService; + +@RunWith(MockitoJUnitRunner.class) +public class ListAccountsCmdTest { + + @Mock + ResourceLimitService resourceLimitService; + + + @Test + public void testGetShowIcon() { + ListAccountsCmd cmd = new ListAccountsCmd(); + ReflectionTestUtils.setField(cmd, "showIcon", null); + Assert.assertFalse(cmd.getShowIcon()); + ReflectionTestUtils.setField(cmd, "showIcon", false); + Assert.assertFalse(cmd.getShowIcon()); + ReflectionTestUtils.setField(cmd, "showIcon", true); + Assert.assertTrue(cmd.getShowIcon()); + } + + @Test + public void testGetTag() { + ListAccountsCmd cmd = new ListAccountsCmd(); + ReflectionTestUtils.setField(cmd, "tag", null); + Assert.assertNull(cmd.getTag()); + String tag = "ABC"; + ReflectionTestUtils.setField(cmd, "tag", tag); + Assert.assertEquals(tag, cmd.getTag()); + } + + @Test + public void testUpdateAccountResponseNoAccounts() { + ListAccountsCmd cmd = new ListAccountsCmd(); + cmd._resourceLimitService = resourceLimitService; + cmd.updateAccountResponse(null); + Mockito.verify(resourceLimitService, Mockito.never()).updateTaggedResourceLimitsAndCountsForAccounts(Mockito.anyList(), Mockito.anyString()); + } + + @Test + public void testUpdateDomainResponseWithAccounts() { + ListAccountsCmd cmd = new ListAccountsCmd(); + cmd._resourceLimitService = resourceLimitService; + ReflectionTestUtils.setField(cmd, "tag", "abc"); + cmd.updateAccountResponse(List.of(Mockito.mock(AccountResponse.class))); + Mockito.verify(resourceLimitService, Mockito.times(1)).updateTaggedResourceLimitsAndCountsForAccounts(Mockito.any(), Mockito.any()); + } + + @Test + public void testUpdateDomainResponseWithAccountsMinDetails() { + ListAccountsCmd cmd = new ListAccountsCmd(); + ReflectionTestUtils.setField(cmd, "viewDetails", List.of(ApiConstants.DomainDetails.min.toString())); + cmd._resourceLimitService = resourceLimitService; + ReflectionTestUtils.setField(cmd, "tag", "abc"); + cmd.updateAccountResponse(List.of(Mockito.mock(AccountResponse.class))); + Mockito.verify(resourceLimitService, Mockito.never()).updateTaggedResourceLimitsAndCountsForAccounts(Mockito.any(), Mockito.any()); + } +} 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/bgp/ListASNumbersCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/bgp/ListASNumbersCmdTest.java new file mode 100644 index 000000000000..9d7f4ef7cf13 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/bgp/ListASNumbersCmdTest.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.bgp; + +import com.cloud.bgp.ASNumber; +import com.cloud.bgp.BGPService; + +import com.cloud.utils.Pair; +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.response.ASNumberResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(MockitoJUnitRunner.class) +public class ListASNumbersCmdTest { + + BGPService bgpService = Mockito.spy(BGPService.class); + ResponseGenerator _responseGenerator = Mockito.spy(ResponseGenerator.class); + + @Test + public void testListASNumbersCmdTest() { + Long zoneId = 1L; + Long asNumberRangeId = 2L; + Integer asNumber = 10; + Long networkId = 11L; + Long vpcId = 12L; + String account = "account"; + Long domainId = 13L; + + ListASNumbersCmd cmd = new ListASNumbersCmd(); + ReflectionTestUtils.setField(cmd, "zoneId", zoneId); + ReflectionTestUtils.setField(cmd, "asNumberRangeId", asNumberRangeId); + ReflectionTestUtils.setField(cmd, "asNumber", asNumber); + ReflectionTestUtils.setField(cmd, "networkId", networkId); + ReflectionTestUtils.setField(cmd, "vpcId", vpcId); + ReflectionTestUtils.setField(cmd, "account", account); + ReflectionTestUtils.setField(cmd, "domainId", domainId); + ReflectionTestUtils.setField(cmd, "allocated", Boolean.TRUE); + + ReflectionTestUtils.setField(cmd,"bgpService", bgpService); + ReflectionTestUtils.setField(cmd,"_responseGenerator", _responseGenerator); + + Assert.assertEquals(zoneId, cmd.getZoneId()); + Assert.assertEquals(asNumberRangeId, cmd.getAsNumberRangeId()); + Assert.assertEquals(asNumber, cmd.getAsNumber()); + Assert.assertEquals(networkId, cmd.getNetworkId()); + Assert.assertEquals(vpcId, cmd.getVpcId()); + Assert.assertEquals(account, cmd.getAccount()); + Assert.assertEquals(domainId, cmd.getDomainId()); + Assert.assertTrue(cmd.getAllocated()); + + List asNumbers = new ArrayList<>(); + ASNumber asn = Mockito.mock(ASNumber.class); + asNumbers.add(asn); + Pair, Integer> pair = new Pair<>(asNumbers, 1); + + ASNumberResponse asNumberResponse = Mockito.mock(ASNumberResponse.class); + Mockito.when(_responseGenerator.createASNumberResponse(asn)).thenReturn(asNumberResponse); + + Mockito.when(bgpService.listASNumbers(cmd)).thenReturn(pair); + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Object response = cmd.getResponseObject(); + Assert.assertTrue(response instanceof ListResponse); + ListResponse listResponse = (ListResponse) response; + Assert.assertEquals(1L, (long) listResponse.getCount()); + Assert.assertTrue(listResponse.getResponses().get(0) instanceof ASNumberResponse); + ASNumberResponse firstResponse = (ASNumberResponse) listResponse.getResponses().get(0); + Assert.assertEquals(asNumberResponse, firstResponse); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/consoleproxy/ListConsoleSessionsCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/consoleproxy/ListConsoleSessionsCmdTest.java new file mode 100644 index 000000000000..47bef14bb615 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/consoleproxy/ListConsoleSessionsCmdTest.java @@ -0,0 +1,124 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License.import org.apache.cloudstack.context.CallContext; +package org.apache.cloudstack.api.command.user.consoleproxy; + +import org.apache.cloudstack.consoleproxy.ConsoleSession; +import com.cloud.user.AccountService; + +import com.cloud.user.UserAccount; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.consoleproxy.ConsoleAccessManager; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class ListConsoleSessionsCmdTest { + @Mock + private AccountService accountServiceMock; + + @Mock + private ConsoleAccessManager consoleAccessManagerMock; + + @Spy + @InjectMocks + private ListConsoleSessionsCmd listConsoleSessionsCmdSpy; + + @Test + public void executeTestApiExecutionShouldCallServiceLayer() { + Mockito.when(consoleAccessManagerMock.listConsoleSessions(listConsoleSessionsCmdSpy)).thenReturn(new ListResponse<>()); + listConsoleSessionsCmdSpy.execute(); + Mockito.verify(consoleAccessManagerMock).listConsoleSessions(listConsoleSessionsCmdSpy); + } + + @Test + public void getEntityOwnerIdTestReturnConsoleSessionIdIfProvided() { + ConsoleSession consoleSessionMock = Mockito.mock(ConsoleSession.class); + long consoleSessionId = 2L; + long accountId = 2L; + + Mockito.when(listConsoleSessionsCmdSpy.getId()).thenReturn(consoleSessionId); + Mockito.when(consoleAccessManagerMock.listConsoleSessionById(consoleSessionId)).thenReturn(consoleSessionMock); + Mockito.when(consoleSessionMock.getAccountId()).thenReturn(accountId); + + Assert.assertEquals(accountId, listConsoleSessionsCmdSpy.getEntityOwnerId()); + } + + @Test + public void getEntityOwnerIdTestReturnAccountIdWhenNoConsoleSessionIdIsProvided() { + long accountId = 2L; + + Mockito.when(listConsoleSessionsCmdSpy.getId()).thenReturn(null); + Mockito.when(listConsoleSessionsCmdSpy.getAccountId()).thenReturn(accountId); + + Assert.assertEquals(accountId, listConsoleSessionsCmdSpy.getEntityOwnerId()); + } + + @Test + public void getEntityOwnerIdTestReturnUserIdWhenNoConsoleSessionIdAndAccountIdAreProvided() { + UserAccount userAccountMock = Mockito.mock(UserAccount.class); + long userId = 2L; + + Mockito.when(listConsoleSessionsCmdSpy.getId()).thenReturn(null); + Mockito.when(listConsoleSessionsCmdSpy.getAccountId()).thenReturn(null); + Mockito.when(listConsoleSessionsCmdSpy.getUserId()).thenReturn(userId); + Mockito.when(accountServiceMock.getUserAccountById(userId)).thenReturn(userAccountMock); + Mockito.when(userAccountMock.getAccountId()).thenReturn(userId); + + Assert.assertEquals(userId, listConsoleSessionsCmdSpy.getEntityOwnerId()); + } + + @Test + public void getEntityOwnerIdTestReturnSystemAccountIdWhenNoConsoleSessionIdAndAccountIdAndUserIdAreProvided() { + long systemAccountId = 1L; + + Mockito.when(listConsoleSessionsCmdSpy.getId()).thenReturn(null); + Mockito.when(listConsoleSessionsCmdSpy.getAccountId()).thenReturn(null); + Mockito.when(listConsoleSessionsCmdSpy.getUserId()).thenReturn(null); + + Assert.assertEquals(systemAccountId, listConsoleSessionsCmdSpy.getEntityOwnerId()); + } + + @Test + public void getEntityOwnerIdTestReturnSystemAccountIdWhenConsoleSessionDoesNotExist() { + long consoleSessionId = 2L; + long systemAccountId = 1L; + + Mockito.when(listConsoleSessionsCmdSpy.getId()).thenReturn(consoleSessionId); + Mockito.when(consoleAccessManagerMock.listConsoleSessionById(consoleSessionId)).thenReturn(null); + + Assert.assertEquals(systemAccountId, listConsoleSessionsCmdSpy.getEntityOwnerId()); + } + + @Test + public void getEntityOwnerIdTestReturnSystemAccountIdWhenUserAccountDoesNotExist() { + long userId = 2L; + long systemAccountId = 1L; + + Mockito.when(listConsoleSessionsCmdSpy.getUserId()).thenReturn(userId); + Mockito.when(accountServiceMock.getUserAccountById(userId)).thenReturn(null); + + Assert.assertEquals(systemAccountId, listConsoleSessionsCmdSpy.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmdTest.java new file mode 100644 index 000000000000..c905974b2be9 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmdTest.java @@ -0,0 +1,91 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.firewall; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.collections.CollectionUtils; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import com.cloud.utils.net.NetUtils; + +@RunWith(MockitoJUnitRunner.class) +public class CreateFirewallRuleCmdTest { + + private void validateAllIp4Cidr(final CreateFirewallRuleCmd cmd) { + Assert.assertTrue(CollectionUtils.isNotEmpty(cmd.getSourceCidrList())); + Assert.assertEquals(1, cmd.getSourceCidrList().size()); + Assert.assertEquals(NetUtils.ALL_IP4_CIDRS, cmd.getSourceCidrList().get(0)); + } + + @Test + public void testGetSourceCidrList_Null() { + final CreateFirewallRuleCmd cmd = new CreateFirewallRuleCmd(); + ReflectionTestUtils.setField(cmd, "cidrlist", null); + validateAllIp4Cidr(cmd); + } + + @Test + public void testGetSourceCidrList_Empty() { + final CreateFirewallRuleCmd cmd = new CreateFirewallRuleCmd(); + ReflectionTestUtils.setField(cmd, "cidrlist", new ArrayList<>()); + validateAllIp4Cidr(cmd); + } + + @Test + public void testGetSourceCidrList_NullFirstElement() { + final CreateFirewallRuleCmd cmd = new CreateFirewallRuleCmd(); + List list = new ArrayList<>(); + list.add(null); + ReflectionTestUtils.setField(cmd, "cidrlist", list); + validateAllIp4Cidr(cmd); + } + + @Test + public void testGetSourceCidrList_EmptyFirstElement() { + final CreateFirewallRuleCmd cmd = new CreateFirewallRuleCmd(); + ReflectionTestUtils.setField(cmd, "cidrlist", Collections.singletonList(" ")); + validateAllIp4Cidr(cmd); + } + + @Test + public void testGetSourceCidrList_Valid() { + final CreateFirewallRuleCmd cmd = new CreateFirewallRuleCmd(); + String cidr = "10.1.1.1/22"; + ReflectionTestUtils.setField(cmd, "cidrlist", Collections.singletonList(cidr)); + Assert.assertTrue(CollectionUtils.isNotEmpty(cmd.getSourceCidrList())); + Assert.assertEquals(1, cmd.getSourceCidrList().size()); + Assert.assertEquals(cidr, cmd.getSourceCidrList().get(0)); + } + + @Test + public void testGetSourceCidrList_EmptyFirstElementButMore() { + final CreateFirewallRuleCmd cmd = new CreateFirewallRuleCmd(); + String cidr = "10.1.1.1/22"; + ReflectionTestUtils.setField(cmd, "cidrlist", Arrays.asList(" ", cidr)); + Assert.assertTrue(CollectionUtils.isNotEmpty(cmd.getSourceCidrList())); + Assert.assertEquals(2, cmd.getSourceCidrList().size()); + Assert.assertEquals(cidr, cmd.getSourceCidrList().get(1)); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/gpu/ListGpuCardsCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/gpu/ListGpuCardsCmdTest.java new file mode 100644 index 000000000000..54e726eadbe0 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/gpu/ListGpuCardsCmdTest.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.user.gpu; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + + +public class ListGpuCardsCmdTest { + + @Test + public void getId() { + ListGpuCardsCmd cmd = new ListGpuCardsCmd(); + Assert.assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + Assert.assertEquals(id, cmd.getId()); + } + + @Test + public void getVendorName() { + ListGpuCardsCmd cmd = new ListGpuCardsCmd(); + Assert.assertNull(cmd.getVendorName()); + String vendorName = "vendor name"; + ReflectionTestUtils.setField(cmd, "vendorName", vendorName); + Assert.assertEquals(vendorName, cmd.getVendorName()); + } + + @Test + public void getVendorId() { + ListGpuCardsCmd cmd = new ListGpuCardsCmd(); + Assert.assertNull(cmd.getVendorId()); + String vendorId = "vendor id"; + ReflectionTestUtils.setField(cmd, "vendorId", vendorId); + Assert.assertEquals(vendorId, cmd.getVendorId()); + } + + @Test + public void getDeviceId() { + ListGpuCardsCmd cmd = new ListGpuCardsCmd(); + Assert.assertNull(cmd.getDeviceId()); + String deviceId = "device id"; + ReflectionTestUtils.setField(cmd, "deviceId", deviceId); + Assert.assertEquals(deviceId, cmd.getDeviceId()); + } + + @Test + public void getDeviceName() { + ListGpuCardsCmd cmd = new ListGpuCardsCmd(); + Assert.assertNull(cmd.getDeviceName()); + String deviceName = "device name"; + ReflectionTestUtils.setField(cmd, "deviceName", deviceName); + Assert.assertEquals(deviceName, cmd.getDeviceName()); + } + + @Test + public void getActiveOnly() { + ListGpuCardsCmd cmd = new ListGpuCardsCmd(); + Assert.assertFalse(cmd.getActiveOnly()); + Boolean activeOnly = true; + ReflectionTestUtils.setField(cmd, "activeOnly", activeOnly); + Assert.assertEquals(activeOnly, cmd.getActiveOnly()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/gpu/ListGpuDevicesCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/gpu/ListGpuDevicesCmdTest.java new file mode 100644 index 000000000000..e1a65ee0ece3 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/gpu/ListGpuDevicesCmdTest.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.user.gpu; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +public class ListGpuDevicesCmdTest { + + @Test + public void getVmId() { + ListGpuDevicesCmd cmd = new ListGpuDevicesCmd(); + Assert.assertNull(cmd.getVmId()); + Long vmId = 1L; + ReflectionTestUtils.setField(cmd, "vmId", vmId); + Assert.assertEquals(vmId, cmd.getVmId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/gpu/ListVgpuProfilesCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/gpu/ListVgpuProfilesCmdTest.java new file mode 100644 index 000000000000..7616abd1f8d5 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/gpu/ListVgpuProfilesCmdTest.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.api.command.user.gpu; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +public class ListVgpuProfilesCmdTest { + + @Test + public void getId() { + ListVgpuProfilesCmd cmd = new ListVgpuProfilesCmd(); + Assert.assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + Assert.assertEquals(id, cmd.getId()); + } + + @Test + public void getName() { + ListVgpuProfilesCmd cmd = new ListVgpuProfilesCmd(); + Assert.assertNull(cmd.getName()); + String name = "Test VGPU Profile"; + ReflectionTestUtils.setField(cmd, "name", name); + Assert.assertEquals(name, cmd.getName()); + } + + @Test + public void getCardId() { + ListVgpuProfilesCmd cmd = new ListVgpuProfilesCmd(); + Assert.assertNull(cmd.getCardId()); + Long cardId = 1L; + ReflectionTestUtils.setField(cmd, "cardId", cardId); + Assert.assertEquals(cardId, cmd.getCardId()); + } + + @Test + public void getActiveOnly() { + ListVgpuProfilesCmd cmd = new ListVgpuProfilesCmd(); + Assert.assertFalse(cmd.getActiveOnly()); + Boolean activeOnly = true; + ReflectionTestUtils.setField(cmd, "activeOnly", activeOnly); + Assert.assertEquals(activeOnly, cmd.getActiveOnly()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCategoriesCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCategoriesCmdTest.java new file mode 100644 index 000000000000..f417dc5f876c --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCategoriesCmdTest.java @@ -0,0 +1,87 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.guest; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import com.cloud.cpu.CPU; + +@RunWith(MockitoJUnitRunner.class) +public class ListGuestOsCategoriesCmdTest { + + @Test + public void testIsFeatured() { + ListGuestOsCategoriesCmd cmd = new ListGuestOsCategoriesCmd(); + Assert.assertNull(cmd.isFeatured()); + ReflectionTestUtils.setField(cmd, "featured", false); + Assert.assertFalse(cmd.isFeatured()); + ReflectionTestUtils.setField(cmd, "featured", true); + Assert.assertTrue(cmd.isFeatured()); + } + + @Test + public void testIsIso() { + ListGuestOsCategoriesCmd cmd = new ListGuestOsCategoriesCmd(); + Assert.assertNull(cmd.isIso()); + ReflectionTestUtils.setField(cmd, "iso", false); + Assert.assertFalse(cmd.isIso()); + ReflectionTestUtils.setField(cmd, "iso", true); + Assert.assertTrue(cmd.isIso()); + } + + @Test + public void testIsVnf() { + ListGuestOsCategoriesCmd cmd = new ListGuestOsCategoriesCmd(); + Assert.assertNull(cmd.isVnf()); + ReflectionTestUtils.setField(cmd, "vnf", false); + Assert.assertFalse(cmd.isVnf()); + ReflectionTestUtils.setField(cmd, "vnf", true); + Assert.assertTrue(cmd.isVnf()); + } + + @Test + public void testGetZoneId() { + ListGuestOsCategoriesCmd cmd = new ListGuestOsCategoriesCmd(); + Assert.assertNull(cmd.getZoneId()); + Long zoneId = 100L; + ReflectionTestUtils.setField(cmd, "zoneId", zoneId); + Assert.assertEquals(zoneId, cmd.getZoneId()); + } + + @Test + public void testGetArch() { + ListGuestOsCategoriesCmd cmd = new ListGuestOsCategoriesCmd(); + Assert.assertNull(cmd.getArch()); + CPU.CPUArch arch = CPU.CPUArch.getDefault(); + ReflectionTestUtils.setField(cmd, "arch", arch.getType()); + Assert.assertEquals(arch, cmd.getArch()); + } + + @Test + public void testIsShowIcon() { + ListGuestOsCategoriesCmd cmd = new ListGuestOsCategoriesCmd(); + Assert.assertFalse(cmd.isShowIcon()); + ReflectionTestUtils.setField(cmd, "showIcon", false); + Assert.assertFalse(cmd.isShowIcon()); + ReflectionTestUtils.setField(cmd, "showIcon", true); + Assert.assertTrue(cmd.isShowIcon()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmdTest.java index 415ee01ba168..c42a7882c545 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmdTest.java @@ -41,7 +41,10 @@ public class UpdateNetworkCmdTest { NetworkService networkService; @Mock public EntityManager _entityMgr; - private ResponseGenerator responseGenerator; + @Mock + private ResponseGenerator _responseGenerator; + @Mock + private Object job; @InjectMocks UpdateNetworkCmd cmd = new UpdateNetworkCmd(); @@ -176,15 +179,13 @@ public void testExecute() throws InsufficientCapacityException { ReflectionTestUtils.setField(cmd, "id", networkId); ReflectionTestUtils.setField(cmd, "publicMtu", publicmtu); Network network = Mockito.mock(Network.class); - responseGenerator = Mockito.mock(ResponseGenerator.class); NetworkResponse response = Mockito.mock(NetworkResponse.class); response.setPublicMtu(publicmtu); Mockito.when(networkService.getNetwork(networkId)).thenReturn(network); Mockito.when(networkService.updateGuestNetwork(cmd)).thenReturn(network); - cmd._responseGenerator = responseGenerator; - Mockito.when(responseGenerator.createNetworkResponse(ResponseObject.ResponseView.Restricted, network)).thenReturn(response); + Mockito.when(_responseGenerator.createNetworkResponse(ResponseObject.ResponseView.Restricted, network)).thenReturn(response); cmd.execute(); - Mockito.verify(responseGenerator).createNetworkResponse(Mockito.any(ResponseObject.ResponseView.class), Mockito.any(Network.class)); + Mockito.verify(_responseGenerator).createNetworkResponse(Mockito.any(ResponseObject.ResponseView.class), Mockito.any(Network.class)); NetworkResponse actualResponse = (NetworkResponse) cmd.getResponseObject(); Assert.assertEquals(response, actualResponse); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/CreateRoutingFirewallRuleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/CreateRoutingFirewallRuleCmdTest.java new file mode 100644 index 000000000000..11c41f4c92d2 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/CreateRoutingFirewallRuleCmdTest.java @@ -0,0 +1,251 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.routing; + +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.firewall.FirewallService; +import com.cloud.network.rules.FirewallRule; +import com.cloud.utils.net.NetUtils; + +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.response.FirewallResponse; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.Arrays; +import java.util.List; + +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; + +@RunWith(MockitoJUnitRunner.class) +public class CreateRoutingFirewallRuleCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + FirewallService _firewallService = Mockito.spy(FirewallService.class); + + ResponseGenerator _responseGenerator = Mockito.spy(ResponseGenerator.class); + + @Test + public void testIsDisplay() { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + assertTrue(cmd.isDisplay()); + + ReflectionTestUtils.setField(cmd, "display", Boolean.TRUE); + assertTrue(cmd.isDisplay()); + + ReflectionTestUtils.setField(cmd, "display", Boolean.FALSE); + assertFalse(cmd.isDisplay()); + } + + @Test + public void testGetProtocolValid() { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + assertEquals("", cmd.getProtocol()); + + ReflectionTestUtils.setField(cmd, "protocol", "1"); + assertEquals(NetUtils.ICMP_PROTO, cmd.getProtocol()); + + ReflectionTestUtils.setField(cmd, "protocol", "icmp"); + assertEquals(NetUtils.ICMP_PROTO, cmd.getProtocol()); + + ReflectionTestUtils.setField(cmd, "protocol", "6"); + assertEquals(NetUtils.TCP_PROTO, cmd.getProtocol()); + + ReflectionTestUtils.setField(cmd, "protocol", "tcp"); + assertEquals(NetUtils.TCP_PROTO, cmd.getProtocol()); + + ReflectionTestUtils.setField(cmd, "protocol", "17"); + assertEquals(NetUtils.UDP_PROTO, cmd.getProtocol()); + + ReflectionTestUtils.setField(cmd, "protocol", "udp"); + assertEquals(NetUtils.UDP_PROTO, cmd.getProtocol()); + } + + @Test(expected = InvalidParameterValueException.class) + public void testGetProtocolInValid() { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + + ReflectionTestUtils.setField(cmd, "protocol", "100"); + cmd.getProtocol(); + } + + @Test + public void testGetSourceCidrListNull() { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + + List result = cmd.getSourceCidrList(); + assertNotNull(result); + assertEquals(1, result.size()); + assertEquals(NetUtils.ALL_IP4_CIDRS, result.get(0)); + } + + @Test + public void testGetSourceCidrList() { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + + List cidrList = Arrays.asList("192.168.0.0/24", "10.0.0.0/8"); + cmd.sourceCidrList = cidrList; + List result = cmd.getSourceCidrList(); + assertNotNull(result); + assertEquals(cidrList, result); + } + + @Test + public void testGetDestinationCidrListNull() { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + + List result = cmd.getDestinationCidrList(); + assertNotNull(result); + assertEquals(1, result.size()); + assertEquals(NetUtils.ALL_IP4_CIDRS, result.get(0)); + } + + @Test + public void testGetDestinationCidrList() { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + + List cidrList = Arrays.asList("192.168.0.0/24", "10.0.0.0/8"); + cmd.destinationCidrlist = cidrList; + List result = cmd.getDestinationCidrList(); + assertNotNull(result); + assertEquals(cidrList, result); + } + + @Test + public void testGetTrafficTypeValid() { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + assertEquals(FirewallRule.TrafficType.Ingress, cmd.getTrafficType()); + + ReflectionTestUtils.setField(cmd, "trafficType", "ingress"); + assertEquals(FirewallRule.TrafficType.Ingress, cmd.getTrafficType()); + + ReflectionTestUtils.setField(cmd, "trafficType", "egress"); + assertEquals(FirewallRule.TrafficType.Egress, cmd.getTrafficType()); + } + + @Test(expected = InvalidParameterValueException.class) + public void testGetTrafficTypeInValid() { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + + ReflectionTestUtils.setField(cmd, "trafficType", "invalid"); + cmd.getTrafficType(); + } + + @Test + public void testSourcePortStartEnd() { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + assertNull(cmd.getSourcePortStart()); + assertNull(cmd.getSourcePortEnd()); + + ReflectionTestUtils.setField(cmd, "publicStartPort", 1111); + assertEquals(1111, (int) cmd.getSourcePortStart()); + assertEquals(1111, (int) cmd.getSourcePortEnd()); + + ReflectionTestUtils.setField(cmd, "publicEndPort", 2222); + assertEquals(1111, (int) cmd.getSourcePortStart()); + assertEquals(2222, (int) cmd.getSourcePortEnd()); + } + + @Test + public void testNetworkId() { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + + ReflectionTestUtils.setField(cmd, "networkId", 1111L); + assertEquals(1111L, (long) cmd.getNetworkId()); + + assertEquals(1111L, (long) cmd.getApiResourceId()); + assertEquals(ApiCommandResourceType.Network, cmd.getApiResourceType()); + assertEquals(EventTypes.EVENT_ROUTING_IPV4_FIREWALL_RULE_CREATE, cmd.getEventType()); + assertEquals("Creating ipv4 firewall rule for routed network", cmd.getEventDescription()); + } + + @Test + public void testIcmpCodeAndType() { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + ReflectionTestUtils.setField(cmd, "protocol", "tcp"); + assertNull(cmd.getIcmpType()); + assertNull(cmd.getIcmpCode()); + + ReflectionTestUtils.setField(cmd, "protocol", "icmp"); + assertEquals(-1, (int) cmd.getIcmpType()); + assertEquals(-1, (int) cmd.getIcmpCode()); + + ReflectionTestUtils.setField(cmd, "icmpType", 1111); + ReflectionTestUtils.setField(cmd, "icmpCode", 2222); + assertEquals(1111, (int) cmd.getIcmpType()); + assertEquals(2222, (int) cmd.getIcmpCode()); + } + + @Test + public void testCreate() throws Exception { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + ReflectionTestUtils.setField(cmd, "routedIpv4Manager", routedIpv4Manager); + + Long id = 1L; + String uuid = "uuid"; + FirewallRule firewallRule = Mockito.spy(FirewallRule.class); + Mockito.when(firewallRule.getId()).thenReturn(id); + Mockito.when(firewallRule.getUuid()).thenReturn(uuid); + Mockito.when(routedIpv4Manager.createRoutingFirewallRule(cmd)).thenReturn(firewallRule); + + try { + cmd.create(); + } catch (Exception ignored) { + } + + assertEquals(id, cmd.getEntityId()); + assertEquals(uuid, cmd.getEntityUuid()); + } + + @Test + public void testExecute() throws Exception { + CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd(); + ReflectionTestUtils.setField(cmd, "routedIpv4Manager", routedIpv4Manager); + ReflectionTestUtils.setField(cmd, "_firewallService", _firewallService); + ReflectionTestUtils.setField(cmd, "_responseGenerator", _responseGenerator); + + Long id = 1L; + FirewallRule firewallRule = Mockito.spy(FirewallRule.class); + Mockito.when(firewallRule.getId()).thenReturn(id); + Mockito.when(_firewallService.getFirewallRule(id)).thenReturn(firewallRule); + Mockito.when(routedIpv4Manager.applyRoutingFirewallRule(id)).thenReturn(true); + + FirewallResponse ruleResponse = Mockito.mock(FirewallResponse.class); + Mockito.when(_responseGenerator.createFirewallResponse(firewallRule)).thenReturn(ruleResponse); + + try { + ReflectionTestUtils.setField(cmd, "id", id); + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(ruleResponse, cmd.getResponseObject()); + } +} 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 new file mode 100644 index 000000000000..d3cf5dd6cd68 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/DeleteRoutingFirewallRuleCmdTest.java @@ -0,0 +1,89 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.user.network.routing; + +import com.cloud.event.EventTypes; +import com.cloud.network.firewall.FirewallService; +import com.cloud.network.rules.FirewallRule; + +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; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.UUID; + +import static org.junit.Assert.assertEquals; + +@RunWith(MockitoJUnitRunner.class) +public class DeleteRoutingFirewallRuleCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + FirewallService _firewallService = Mockito.spy(FirewallService.class); + + @Test + public void testProperties() { + DeleteRoutingFirewallRuleCmd cmd = new DeleteRoutingFirewallRuleCmd(); + ReflectionTestUtils.setField(cmd, "_firewallService", _firewallService); + + long id = 1L; + UUID uuid = UUID.randomUUID(); + long accountId = 2L; + long networkId = 3L; + + FirewallRule firewallRule = Mockito.spy(FirewallRule.class); + Mockito.when(firewallRule.getAccountId()).thenReturn(accountId); + Mockito.when(firewallRule.getNetworkId()).thenReturn(networkId); + 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 with ID: %s", uuid), cmd.getEventDescription()); + } + + + @Test + public void testExecute() throws Exception { + DeleteRoutingFirewallRuleCmd cmd = new DeleteRoutingFirewallRuleCmd(); + ReflectionTestUtils.setField(cmd, "routedIpv4Manager", routedIpv4Manager); + + Long id = 1L; + Mockito.when(routedIpv4Manager.revokeRoutingFirewallRule(id)).thenReturn(true); + + try { + ReflectionTestUtils.setField(cmd, "id", id); + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertTrue(cmd.getResponseObject() instanceof SuccessResponse); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/ListRoutingFirewallRulesCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/ListRoutingFirewallRulesCmdTest.java new file mode 100644 index 000000000000..53ac45917cb8 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/ListRoutingFirewallRulesCmdTest.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.network.routing; + +import com.cloud.network.rules.FirewallRule; +import com.cloud.utils.Pair; + +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.response.FirewallResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +@RunWith(MockitoJUnitRunner.class) +public class ListRoutingFirewallRulesCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + ResponseGenerator _responseGenerator = Mockito.spy(ResponseGenerator.class); + + @Test + public void testIsDisplay() { + ListRoutingFirewallRulesCmd cmd = new ListRoutingFirewallRulesCmd(); + assertTrue(cmd.getDisplay()); + + ReflectionTestUtils.setField(cmd, "display", Boolean.TRUE); + assertTrue(cmd.getDisplay()); + + ReflectionTestUtils.setField(cmd, "display", Boolean.FALSE); + assertFalse(cmd.getDisplay()); + } + + @Test + public void testTrafficType() { + ListRoutingFirewallRulesCmd cmd = new ListRoutingFirewallRulesCmd(); + assertNull(cmd.getTrafficType()); + + ReflectionTestUtils.setField(cmd, "trafficType", "Ingress"); + assertEquals(FirewallRule.TrafficType.Ingress, cmd.getTrafficType()); + + ReflectionTestUtils.setField(cmd, "trafficType", "Egress"); + assertEquals(FirewallRule.TrafficType.Egress, cmd.getTrafficType()); + } + + @Test + public void testOtherProperties() { + ListRoutingFirewallRulesCmd cmd = new ListRoutingFirewallRulesCmd(); + + long id = 1L; + long networkId = 3L; + + ReflectionTestUtils.setField(cmd, "id", id); + ReflectionTestUtils.setField(cmd, "networkId", networkId); + + assertEquals(id, (long) cmd.getId()); + assertEquals(networkId, (long) cmd.getNetworkId()); + assertNull(cmd.getIpAddressId()); + } + + + @Test + public void testExecute() throws Exception { + ListRoutingFirewallRulesCmd cmd = new ListRoutingFirewallRulesCmd(); + ReflectionTestUtils.setField(cmd, "routedIpv4Manager", routedIpv4Manager); + ReflectionTestUtils.setField(cmd, "_responseGenerator", _responseGenerator); + + Long id = 1L; + FirewallRule firewallRule = Mockito.spy(FirewallRule.class); + List firewallRules = Arrays.asList(firewallRule); + Pair, Integer> result = new Pair<>(firewallRules, 1); + + Mockito.when(routedIpv4Manager.listRoutingFirewallRules(cmd)).thenReturn(result); + + FirewallResponse ruleResponse = Mockito.mock(FirewallResponse.class); + Mockito.when(_responseGenerator.createFirewallResponse(firewallRule)).thenReturn(ruleResponse); + + try { + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertTrue(cmd.getResponseObject() instanceof ListResponse); + ListResponse listResponse = (ListResponse) cmd.getResponseObject(); + Assert.assertEquals(1, (int) listResponse.getCount()); + Assert.assertEquals(ruleResponse, listResponse.getResponses().get(0)); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/UpdateRoutingFirewallRuleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/UpdateRoutingFirewallRuleCmdTest.java new file mode 100644 index 000000000000..dd0319df6960 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/network/routing/UpdateRoutingFirewallRuleCmdTest.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 +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.routing; + +import com.cloud.event.EventTypes; +import com.cloud.network.firewall.FirewallService; +import com.cloud.network.rules.FirewallRule; + +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.response.FirewallResponse; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +@RunWith(MockitoJUnitRunner.class) +public class UpdateRoutingFirewallRuleCmdTest { + + RoutedIpv4Manager routedIpv4Manager = Mockito.spy(RoutedIpv4Manager.class); + + FirewallService _firewallService = Mockito.spy(FirewallService.class); + + ResponseGenerator _responseGenerator = Mockito.spy(ResponseGenerator.class); + + @Test + public void testIsDisplay() { + UpdateRoutingFirewallRuleCmd cmd = new UpdateRoutingFirewallRuleCmd(); + assertTrue(cmd.isDisplay()); + + ReflectionTestUtils.setField(cmd, "display", Boolean.TRUE); + assertTrue(cmd.isDisplay()); + + ReflectionTestUtils.setField(cmd, "display", Boolean.FALSE); + assertFalse(cmd.isDisplay()); + } + + @Test + public void testOtherProperties() { + UpdateRoutingFirewallRuleCmd cmd = new UpdateRoutingFirewallRuleCmd(); + ReflectionTestUtils.setField(cmd, "_firewallService", _firewallService); + + long id = 1L; + long accountId = 2L; + long networkId = 3L; + + FirewallRule firewallRule = Mockito.spy(FirewallRule.class); + Mockito.when(firewallRule.getAccountId()).thenReturn(accountId); + Mockito.when(firewallRule.getNetworkId()).thenReturn(networkId); + Mockito.when(_firewallService.getFirewallRule(id)).thenReturn(firewallRule); + + ReflectionTestUtils.setField(cmd, "id", id); + 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_UPDATE, cmd.getEventType()); + assertEquals("Updating ipv4 routing firewall rule", cmd.getEventDescription()); + } + + + @Test + public void testExecute() throws Exception { + UpdateRoutingFirewallRuleCmd cmd = new UpdateRoutingFirewallRuleCmd(); + ReflectionTestUtils.setField(cmd, "routedIpv4Manager", routedIpv4Manager); + ReflectionTestUtils.setField(cmd, "_firewallService", _firewallService); + ReflectionTestUtils.setField(cmd, "_responseGenerator", _responseGenerator); + + Long id = 1L; + FirewallRule firewallRule = Mockito.spy(FirewallRule.class); + Mockito.when(routedIpv4Manager.updateRoutingFirewallRule(cmd)).thenReturn(firewallRule); + + FirewallResponse ruleResponse = Mockito.mock(FirewallResponse.class); + Mockito.when(_responseGenerator.createFirewallResponse(firewallRule)).thenReturn(ruleResponse); + + try { + ReflectionTestUtils.setField(cmd, "id", id); + cmd.execute(); + } catch (Exception ignored) { + } + + Assert.assertEquals(ruleResponse, cmd.getResponseObject()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/offering/ListDiskOfferingsCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/offering/ListDiskOfferingsCmdTest.java new file mode 100644 index 000000000000..598fdd8ac4f6 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/offering/ListDiskOfferingsCmdTest.java @@ -0,0 +1,37 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.offering; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class ListDiskOfferingsCmdTest { + + @Test + public void testGetVirtualMachineId() { + ListDiskOfferingsCmd cmd = new ListDiskOfferingsCmd(); + ReflectionTestUtils.setField(cmd, "virtualMachineId", null); + Assert.assertNull(cmd.getVirtualMachineId()); + Long id = 100L; + ReflectionTestUtils.setField(cmd, "virtualMachineId", id); + Assert.assertEquals(id, cmd.getVirtualMachineId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/offering/ListServiceOfferingsCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/offering/ListServiceOfferingsCmdTest.java new file mode 100644 index 000000000000..f408132e5fce --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/offering/ListServiceOfferingsCmdTest.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.user.offering; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + + +@RunWith(MockitoJUnitRunner.class) +public class ListServiceOfferingsCmdTest { + + @Test + public void testGetTemplateId() { + ListServiceOfferingsCmd cmd = new ListServiceOfferingsCmd(); + ReflectionTestUtils.setField(cmd, "templateId", null); + Assert.assertNull(cmd.getTemplateId()); + Long id = 100L; + ReflectionTestUtils.setField(cmd, "templateId", id); + Assert.assertEquals(id, cmd.getTemplateId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/resource/ListResourceLimitsCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/resource/ListResourceLimitsCmdTest.java new file mode 100644 index 000000000000..3e999be2cfd7 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/resource/ListResourceLimitsCmdTest.java @@ -0,0 +1,37 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.resource; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class ListResourceLimitsCmdTest { + + @Test + public void testGetTag() { + ListResourceLimitsCmd cmd = new ListResourceLimitsCmd(); + ReflectionTestUtils.setField(cmd, "tag", null); + Assert.assertNull(cmd.getTag()); + String tag = "ABC"; + ReflectionTestUtils.setField(cmd, "tag", tag); + Assert.assertEquals(tag, cmd.getTag()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/resource/UpdateResourceCountCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/resource/UpdateResourceCountCmdTest.java new file mode 100644 index 000000000000..ab7d3253ab52 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/resource/UpdateResourceCountCmdTest.java @@ -0,0 +1,37 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.resource; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class UpdateResourceCountCmdTest { + + @Test + public void testGetTag() { + UpdateResourceCountCmd cmd = new UpdateResourceCountCmd(); + ReflectionTestUtils.setField(cmd, "tag", null); + Assert.assertNull(cmd.getTag()); + String tag = "ABC"; + ReflectionTestUtils.setField(cmd, "tag", tag); + Assert.assertEquals(tag, cmd.getTag()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/resource/UpdateResourceLimitCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/resource/UpdateResourceLimitCmdTest.java new file mode 100644 index 000000000000..dff27a200446 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/resource/UpdateResourceLimitCmdTest.java @@ -0,0 +1,37 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.resource; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class UpdateResourceLimitCmdTest { + + @Test + public void testGetTag() { + UpdateResourceLimitCmd cmd = new UpdateResourceLimitCmd(); + ReflectionTestUtils.setField(cmd, "tag", null); + Assert.assertNull(cmd.getTag()); + String tag = "ABC"; + ReflectionTestUtils.setField(cmd, "tag", tag); + Assert.assertEquals(tag, cmd.getTag()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/snapshot/CopySnapshotCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/snapshot/CopySnapshotCmdTest.java index 632496ad2151..db27cc76ec9f 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/user/snapshot/CopySnapshotCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/snapshot/CopySnapshotCmdTest.java @@ -87,7 +87,12 @@ public void testGetDestZoneIdWithBothParams() { @Test (expected = ServerApiException.class) public void testExecuteWrongNoParams() { + UUIDManager uuidManager = Mockito.mock(UUIDManager.class); + SnapshotApiService snapshotApiService = Mockito.mock(SnapshotApiService.class); final CopySnapshotCmd cmd = new CopySnapshotCmd(); + cmd._uuidMgr = uuidManager; + cmd._snapshotService = snapshotApiService; + try { cmd.execute(); } catch (ResourceUnavailableException e) { diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/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/template/UpdateTemplateCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/template/UpdateTemplateCmdTest.java new file mode 100644 index 000000000000..03e558b58a8e --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/template/UpdateTemplateCmdTest.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.api.command.user.template; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import com.cloud.storage.Storage; + +@RunWith(MockitoJUnitRunner.class) +public class UpdateTemplateCmdTest { + + @Test + public void testGetTemplateType() { + UpdateTemplateCmd cmd = new UpdateTemplateCmd(); + ReflectionTestUtils.setField(cmd, "templateType", null); + Assert.assertNull(cmd.getTemplateType()); + String type = Storage.TemplateType.ROUTING.toString(); + ReflectionTestUtils.setField(cmd, "templateTag", type); + Assert.assertEquals(type, cmd.getTemplateTag()); + } + + @Test + public void testGetTemplateTag() { + UpdateTemplateCmd cmd = new UpdateTemplateCmd(); + ReflectionTestUtils.setField(cmd, "templateTag", null); + Assert.assertNull(cmd.getTemplateTag()); + String tag = "ABC"; + ReflectionTestUtils.setField(cmd, "templateTag", tag); + Assert.assertEquals(tag, cmd.getTemplateTag()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/userdata/ListUserDataCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/userdata/ListUserDataCmdTest.java index 3f47a078445c..8b7db2924629 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/user/userdata/ListUserDataCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/userdata/ListUserDataCmdTest.java @@ -68,7 +68,7 @@ public void testListSuccess() { Pair, Integer> result = new Pair, Integer>(userDataList, 1); UserDataResponse userDataResponse = Mockito.mock(UserDataResponse.class); - Mockito.when(_mgr.listUserDatas(cmd)).thenReturn(result); + Mockito.when(_mgr.listUserDatas(cmd, false)).thenReturn(result); Mockito.when(_responseGenerator.createUserDataResponse(userData)).thenReturn(userDataResponse); cmd.execute(); @@ -82,7 +82,7 @@ public void testEmptyList() { List userDataList = new ArrayList(); Pair, Integer> result = new Pair, Integer>(userDataList, 0); - Mockito.when(_mgr.listUserDatas(cmd)).thenReturn(result); + Mockito.when(_mgr.listUserDatas(cmd, false)).thenReturn(result); cmd.execute(); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/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 c9eb672c9e94..99bc9d2b3fb7 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 @@ -32,8 +32,6 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import java.security.InvalidParameterException; - public class CreateVMScheduleCmdTest { @Mock public VMScheduleManager vmScheduleManager; @@ -70,11 +68,11 @@ public void testSuccessfulExecution() { /** * given: "We have a VMScheduleManager and CreateVMScheduleCmd" * when: "CreateVMScheduleCmd is executed with an invalid parameter" - * then: "an InvalidParameterException is thrown" + * then: "an InvalidParameterValueException is thrown" */ - @Test(expected = InvalidParameterException.class) - public void testInvalidParameterException() { - Mockito.when(vmScheduleManager.createSchedule(createVMScheduleCmd)).thenThrow(InvalidParameterException.class); + @Test(expected = InvalidParameterValueException.class) + public void testInvalidParameterValueException() { + Mockito.when(vmScheduleManager.createSchedule(createVMScheduleCmd)).thenThrow(InvalidParameterValueException.class); createVMScheduleCmd.execute(); } @@ -94,7 +92,7 @@ public void testSuccessfulGetEntityOwnerId() { /** * given: "We have an EntityManager and CreateVMScheduleCmd" * when: "CreateVMScheduleCmd.getEntityOwnerId is executed for a VM which doesn't exist" - * then: "an InvalidParameterException is thrown" + * then: "an InvalidParameterValueException is thrown" */ @Test(expected = InvalidParameterValueException.class) public void testFailureGetEntityOwnerId() { 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 9b4decc83aa6..1f764a84365f 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 @@ -34,8 +34,6 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import java.security.InvalidParameterException; - public class DeleteVMScheduleCmdTest { @Mock public VMScheduleManager vmScheduleManager; @@ -89,11 +87,11 @@ public void testServerApiException() { /** * given: "We have a VMScheduleManager and DeleteVMScheduleCmd" * when: "DeleteVMScheduleCmd is executed with an invalid parameter" - * then: "an InvalidParameterException is thrown" + * then: "an InvalidParameterValueException is thrown" */ - @Test(expected = InvalidParameterException.class) - public void testInvalidParameterException() { - Mockito.when(vmScheduleManager.removeSchedule(deleteVMScheduleCmd)).thenThrow(InvalidParameterException.class); + @Test(expected = InvalidParameterValueException.class) + public void testInvalidParameterValueException() { + Mockito.when(vmScheduleManager.removeSchedule(deleteVMScheduleCmd)).thenThrow(InvalidParameterValueException.class); deleteVMScheduleCmd.execute(); } @@ -113,7 +111,7 @@ public void testSuccessfulGetEntityOwnerId() { /** * given: "We have an EntityManager and DeleteVMScheduleCmd" * when: "DeleteVMScheduleCmd.getEntityOwnerId is executed for a VM which doesn't exist" - * then: "an InvalidParameterException is thrown" + * then: "an InvalidParameterValueException is thrown" */ @Test(expected = InvalidParameterValueException.class) public void testFailureGetEntityOwnerId() { diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmdTest.java new file mode 100644 index 000000000000..f7e3e38d9c3f --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmdTest.java @@ -0,0 +1,483 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.vm; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiConstants.BootMode; +import org.apache.cloudstack.api.ApiConstants.BootType; +import org.apache.cloudstack.api.ApiConstants.IoDriverPolicy; +import org.apache.cloudstack.vm.lease.VMLeaseManager; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.NetworkService; +import com.cloud.utils.db.EntityManager; +import com.cloud.vm.VmDetailConstants; +import com.cloud.network.Network; +import com.cloud.template.VirtualMachineTemplate; +import com.cloud.offering.DiskOffering; +import com.cloud.network.Network.IpAddresses; +import com.cloud.vm.VmDiskInfo; + +@RunWith(MockitoJUnitRunner.class) +public class DeployVMCmdTest { + + @Spy + private DeployVMCmd cmd = new DeployVMCmd(); + + @Test + public void testGetBootType_ValidUEFI() { + ReflectionTestUtils.setField(cmd, "bootType", "UEFI"); + + BootType result = cmd.getBootType(); + + assertEquals(BootType.UEFI, result); + } + + @Test + public void testGetBootTypeValidBIOS() { + ReflectionTestUtils.setField(cmd, "bootType", "BIOS"); + + BootType result = cmd.getBootType(); + + assertEquals(BootType.BIOS, result); + } + + @Test + public void testGetBootTypeInvalidValue() { + ReflectionTestUtils.setField(cmd, "bootType", "INVALID"); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getBootType(); + }); + assertTrue(thrownException.getMessage().contains("Invalid bootType INVALID")); + } + + @Test + public void testGetBootTypeNullValue() { + ReflectionTestUtils.setField(cmd, "bootType", null); + + BootType result = cmd.getBootType(); + + assertNull(result); + } + + @Test + public void testGetBootModeValidSecure() { + ReflectionTestUtils.setField(cmd, "bootMode", "SECURE"); + ReflectionTestUtils.setField(cmd, "bootType", "UEFI"); + + BootMode result = cmd.getBootMode(); + + assertEquals(BootMode.SECURE, result); + } + + @Test + public void testGetBootModeValidLegacy() { + ReflectionTestUtils.setField(cmd, "bootMode", "LEGACY"); + ReflectionTestUtils.setField(cmd, "bootType", "UEFI"); + + BootMode result = cmd.getBootMode(); + + assertEquals(BootMode.LEGACY, result); + } + + @Test + public void testGetBootModeInvalidValue() { + ReflectionTestUtils.setField(cmd, "bootMode", "INVALID"); + ReflectionTestUtils.setField(cmd, "bootType", "UEFI"); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getBootMode(); + }); + assertTrue(thrownException.getMessage().contains("Invalid bootmode: INVALID specified for VM: null. Valid values are: [LEGACY, SECURE]")); + } + + @Test + public void testGetBootModeUEFIWithoutBootMode() { + ReflectionTestUtils.setField(cmd, "bootMode", null); + ReflectionTestUtils.setField(cmd, "bootType", "UEFI"); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getBootMode(); + }); + assertTrue(thrownException.getMessage().contains("bootmode must be specified for the VM with boot type: UEFI. Valid values are: [LEGACY, SECURE]")); + } + + @Test + public void testGetDetails() { + ReflectionTestUtils.setField(cmd, "bootType", "UEFI"); + ReflectionTestUtils.setField(cmd, "bootMode", "SECURE"); + ReflectionTestUtils.setField(cmd, "rootdisksize", 100L); + ReflectionTestUtils.setField(cmd, "ioDriverPolicy", "native"); + ReflectionTestUtils.setField(cmd, "iothreadsEnabled", true); + ReflectionTestUtils.setField(cmd, "nicMultiqueueNumber", null); + ReflectionTestUtils.setField(cmd, "nicPackedVirtQueues", null); + ReflectionTestUtils.setField(cmd, "details", new HashMap<>()); + + Map result = cmd.getDetails(); + + assertEquals("SECURE", result.get("UEFI")); + assertEquals("100", result.get(VmDetailConstants.ROOT_DISK_SIZE)); + assertEquals("native", result.get(VmDetailConstants.IO_POLICY)); + assertEquals("true", result.get(VmDetailConstants.IOTHREADS)); + } + + @Test + public void testGetLeaseExpiryActionValidStop() { + ReflectionTestUtils.setField(cmd, "leaseExpiryAction", "STOP"); + + VMLeaseManager.ExpiryAction result = cmd.getLeaseExpiryAction(); + + assertEquals(VMLeaseManager.ExpiryAction.STOP, result); + } + + @Test + public void testGetLeaseExpiryActionValidDestroy() { + ReflectionTestUtils.setField(cmd, "leaseExpiryAction", "DESTROY"); + + VMLeaseManager.ExpiryAction result = cmd.getLeaseExpiryAction(); + + assertEquals(VMLeaseManager.ExpiryAction.DESTROY, result); + } + + @Test + public void testGetLeaseExpiryActionInvalidValue() { + ReflectionTestUtils.setField(cmd, "leaseExpiryAction", "INVALID"); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getLeaseExpiryAction(); + }); + assertTrue(thrownException.getMessage().contains("Invalid value configured for leaseexpiryaction")); + } + + @Test + public void testGetLeaseExpiryActionNullValue() { + ReflectionTestUtils.setField(cmd, "leaseExpiryAction", null); + + VMLeaseManager.ExpiryAction result = cmd.getLeaseExpiryAction(); + + assertNull(result); + } + + @Test + public void testGetIoDriverPolicyValidThrottle() { + ReflectionTestUtils.setField(cmd, "ioDriverPolicy", "native"); + + IoDriverPolicy result = cmd.getIoDriverPolicy(); + + assertEquals(IoDriverPolicy.valueOf("NATIVE"), result); + } + + @Test + public void testGetIoDriverPolicyInvalidValue() { + ReflectionTestUtils.setField(cmd, "ioDriverPolicy", "INVALID"); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getIoDriverPolicy(); + }); + assertTrue(thrownException.getMessage().contains("Invalid io policy INVALID")); + } + + @Test + public void testGetNetworkIds() { + List networkIds = Arrays.asList(1L, 2L, 3L); + ReflectionTestUtils.setField(cmd, "networkIds", networkIds); + ReflectionTestUtils.setField(cmd, "vAppNetworks", null); + ReflectionTestUtils.setField(cmd, "ipToNetworkList", null); + + List result = cmd.getNetworkIds(); + + assertEquals(networkIds, result); + } + + @Test + public void testGetNetworkIdsVAppNetworks() { + Map vAppNetworks = new HashMap<>(); + vAppNetworks.put("network1", new HashMap()); + ReflectionTestUtils.setField(cmd, "vAppNetworks", vAppNetworks); + ReflectionTestUtils.setField(cmd, "networkIds", null); + ReflectionTestUtils.setField(cmd, "ipToNetworkList", null); + ReflectionTestUtils.setField(cmd, "ipAddress", null); + ReflectionTestUtils.setField(cmd, "ip6Address", null); + + List result = cmd.getNetworkIds(); + + assertTrue(result.isEmpty()); + } + + @Test + public void testGetNetworkIdsVAppNetworksAndNetworkIds() { + Map vAppNetworks = new HashMap<>(); + vAppNetworks.put("network1", new HashMap()); + ReflectionTestUtils.setField(cmd, "vAppNetworks", vAppNetworks); + ReflectionTestUtils.setField(cmd, "networkIds", Arrays.asList(1L, 2L)); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getNetworkIds(); + }); + assertTrue(thrownException.getMessage().contains("nicnetworklist can't be specified along with networkids")); + } + + @Test + public void testGetNetworkIdsIpToNetworkListAndNetworkIds() { + Map ipToNetworkList = new HashMap<>(); + ipToNetworkList.put("0", new HashMap()); + ReflectionTestUtils.setField(cmd, "ipToNetworkList", ipToNetworkList); + ReflectionTestUtils.setField(cmd, "networkIds", Arrays.asList(1L, 2L)); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getNetworkIds(); + }); + assertTrue(thrownException.getMessage().contains("ipToNetworkMap can't be specified along with networkIds or ipAddress")); + } + + @Test + public void testGetIpToNetworkMap_WithNetworkIds() { + ReflectionTestUtils.setField(cmd, "networkIds", Arrays.asList(1L, 2L)); + ReflectionTestUtils.setField(cmd, "ipToNetworkList", new HashMap<>()); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getIpToNetworkMap(); + }); + assertTrue(thrownException.getMessage().contains("NetworkIds and ipAddress can't be specified along with ipToNetworkMap parameter")); + } + + @Test + public void testGetIpToNetworkMap_WithIpAddress() { + ReflectionTestUtils.setField(cmd, "ipAddress", "192.168.1.1"); + ReflectionTestUtils.setField(cmd, "ipToNetworkList", new HashMap<>()); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getIpToNetworkMap(); + }); + assertTrue(thrownException.getMessage().contains("NetworkIds and ipAddress can't be specified along with ipToNetworkMap parameter")); + } + + @Test + public void testGetIpToNetworkMap_WithEmptyIpToNetworkList() { + ReflectionTestUtils.setField(cmd, "networkIds", null); + ReflectionTestUtils.setField(cmd, "ipAddress", null); + ReflectionTestUtils.setField(cmd, "ipToNetworkList", new HashMap<>()); + + Map result = cmd.getIpToNetworkMap(); + + assertNull(result); + } + + @Test + public void testGetIpToNetworkMap_WithNullIpToNetworkList() { + ReflectionTestUtils.setField(cmd, "networkIds", null); + ReflectionTestUtils.setField(cmd, "ipAddress", null); + ReflectionTestUtils.setField(cmd, "ipToNetworkList", null); + + Map result = cmd.getIpToNetworkMap(); + + assertNull(result); + } + + @Test + public void testGetDataDiskInfoList() { + Map dataDisksDetails = new HashMap<>(); + Map dataDisk = new HashMap<>(); + dataDisk.put(ApiConstants.DISK_OFFERING_ID, "offering-uuid"); + dataDisk.put(ApiConstants.DEVICE_ID, "0"); + dataDisk.put(ApiConstants.MIN_IOPS, "1000"); + dataDisk.put(ApiConstants.MAX_IOPS, "2000"); + dataDisksDetails.put("0", dataDisk); + + ReflectionTestUtils.setField(cmd, "dataDisksDetails", dataDisksDetails); + + EntityManager entityMgr = mock(EntityManager.class); + ReflectionTestUtils.setField(cmd, "_entityMgr", entityMgr); + DiskOffering diskOffering = mock(DiskOffering.class); + when(diskOffering.getDiskSize()).thenReturn(1024 * 1024 * 1024L); + when(diskOffering.isCustomizedIops()).thenReturn(true); + when(entityMgr.findByUuid(DiskOffering.class, "offering-uuid")).thenReturn(diskOffering); + + List result = cmd.getDataDiskInfoList(); + + assertNotNull(result); + assertEquals(1, result.size()); + assertEquals(diskOffering, result.get(0).getDiskOffering()); + assertEquals(1L, result.get(0).getSize().longValue()); + assertEquals(1000L, result.get(0).getMinIops().longValue()); + assertEquals(2000L, result.get(0).getMaxIops().longValue()); + } + + @Test + public void testGetIpAddressesFromIpMap() { + Map ipToNetworkList = new HashMap<>(); + Map ipMap = new HashMap<>(); + ipMap.put("ip", "192.168.1.100"); + ipMap.put("mac", "00:11:22:33:44:55"); + ipMap.put("networkid", "1"); + ipToNetworkList.put("0", ipMap); + + ReflectionTestUtils.setField(cmd, "ipToNetworkList", ipToNetworkList); + ReflectionTestUtils.setField(cmd, "networkIds", null); + ReflectionTestUtils.setField(cmd, "ipAddress", null); + + Network mockNetwork = mock(Network.class); + NetworkService networkServiceMock = mock(NetworkService.class); + ReflectionTestUtils.setField(cmd, "_networkService", networkServiceMock); + + Map result = cmd.getIpToNetworkMap(); + + assertNotNull(result); + assertTrue(result.containsKey(1L)); + assertEquals(result.get(1L).getIp4Address(), "192.168.1.100"); + assertEquals(result.get(1L).getMacAddress(), "00:11:22:33:44:55"); + } + + @Test + public void testGetIpAddressesFromIpMapInvalidMac() { + Map ipToNetworkList = new HashMap<>(); + Map ipMap = new HashMap<>(); + ipMap.put("ip", "192.168.1.100"); + ipMap.put("mac", "invalid-mac"); + ipMap.put("networkid", "1"); + ipToNetworkList.put("0", ipMap); + + ReflectionTestUtils.setField(cmd, "ipToNetworkList", ipToNetworkList); + ReflectionTestUtils.setField(cmd, "networkIds", null); + ReflectionTestUtils.setField(cmd, "ipAddress", null); + + Network mockNetwork = mock(Network.class); + NetworkService networkServiceMock = mock(NetworkService.class); + ReflectionTestUtils.setField(cmd, "_networkService", networkServiceMock); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getIpToNetworkMap(); + }); + assertTrue(thrownException.getMessage().contains("Mac address is not valid")); + } + + @Test + public void testGetDhcpOptionsMap() { + Map dhcpOptionsNetworkList = new HashMap<>(); + Map dhcpOptions = new HashMap<>(); + dhcpOptions.put("networkid", "network-1"); + dhcpOptions.put("dhcp:114", "url-value"); + dhcpOptions.put("dhcp:66", "www.test.com"); + dhcpOptionsNetworkList.put("0", dhcpOptions); + + ReflectionTestUtils.setField(cmd, "dhcpOptionsNetworkList", dhcpOptionsNetworkList); + + Map> result = cmd.getDhcpOptionsMap(); + + assertNotNull(result); + assertTrue(result.containsKey("network-1")); + Map networkOptions = result.get("network-1"); + assertEquals("url-value", networkOptions.get(114)); + assertEquals("www.test.com", networkOptions.get(66)); + } + + @Test + public void testGetDhcpOptionsMap_WithMissingNetworkId() { + Map dhcpOptionsNetworkList = new HashMap<>(); + Map dhcpOptions = new HashMap<>(); + dhcpOptions.put("dhcp:114", "url-value"); + dhcpOptionsNetworkList.put("0", dhcpOptions); + + ReflectionTestUtils.setField(cmd, "dhcpOptionsNetworkList", dhcpOptionsNetworkList); + + IllegalArgumentException thrownException = assertThrows(IllegalArgumentException.class, () -> { + cmd.getDhcpOptionsMap(); + }); + assertTrue(thrownException.getMessage().contains("No networkid specified when providing extra dhcp options")); + } + + @Test + public void testGetDataDiskTemplateToDiskOfferingMap() { + ReflectionTestUtils.setField(cmd, "diskOfferingId", null); + + Map dataDiskTemplateToDiskOfferingList = new HashMap<>(); + Map dataDiskTemplate = new HashMap<>(); + dataDiskTemplate.put("datadisktemplateid", "template-uuid"); + dataDiskTemplate.put("diskofferingid", "offering-uuid"); + dataDiskTemplateToDiskOfferingList.put("0", dataDiskTemplate); + + ReflectionTestUtils.setField(cmd, "dataDiskTemplateToDiskOfferingList", dataDiskTemplateToDiskOfferingList); + + VirtualMachineTemplate mockTemplate = mock(VirtualMachineTemplate.class); + when(mockTemplate.getId()).thenReturn(1L); + + DiskOffering mockOffering = mock(DiskOffering.class); + + EntityManager entityMgr = mock(EntityManager.class); + ReflectionTestUtils.setField(cmd, "_entityMgr", entityMgr); + when(entityMgr.findByUuid(VirtualMachineTemplate.class, "template-uuid")).thenReturn(mockTemplate); + when(entityMgr.findByUuid(DiskOffering.class, "offering-uuid")).thenReturn(mockOffering); + + Map result = cmd.getDataDiskTemplateToDiskOfferingMap(); + + assertNotNull(result); + assertEquals(mockOffering, result.get(1L)); + } + + @Test + public void testGetDataDiskTemplateToDiskOfferingMapWithDiskOfferingId() { + ReflectionTestUtils.setField(cmd, "diskOfferingId", 1L); + ReflectionTestUtils.setField(cmd, "dataDiskTemplateToDiskOfferingList", new HashMap<>()); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getDataDiskTemplateToDiskOfferingMap(); + }); + assertTrue(thrownException.getMessage().contains("diskofferingid parameter can't be specified along with datadisktemplatetodiskofferinglist parameter")); + } + + @Test + public void testGetDataDiskTemplateToDiskOfferingMapInvalidTemplateId() { + ReflectionTestUtils.setField(cmd, "diskOfferingId", null); + + Map dataDiskTemplateToDiskOfferingList = new HashMap<>(); + Map dataDiskTemplate = new HashMap<>(); + dataDiskTemplate.put("datadisktemplateid", "invalid-template"); + dataDiskTemplate.put("diskofferingid", "offering-uuid"); + dataDiskTemplateToDiskOfferingList.put("0", dataDiskTemplate); + + ReflectionTestUtils.setField(cmd, "dataDiskTemplateToDiskOfferingList", dataDiskTemplateToDiskOfferingList); + + EntityManager entityMgr = mock(EntityManager.class); + ReflectionTestUtils.setField(cmd, "_entityMgr", entityMgr); + when(entityMgr.findByUuid(VirtualMachineTemplate.class, "invalid-template")).thenReturn(null); + when(entityMgr.findById(VirtualMachineTemplate.class, "invalid-template")).thenReturn(null); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getDataDiskTemplateToDiskOfferingMap(); + }); + assertTrue(thrownException.getMessage().contains("Unable to translate and find entity with datadisktemplateid")); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/vm/ListVMScheduleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/ListVMScheduleCmdTest.java index f9a1d945f03a..f5434de3581c 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 @@ -18,6 +18,7 @@ */ package org.apache.cloudstack.api.command.user.vm; +import com.cloud.exception.InvalidParameterValueException; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.VMScheduleResponse; import org.apache.cloudstack.vm.schedule.VMScheduleManager; @@ -30,7 +31,6 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import java.security.InvalidParameterException; import java.util.ArrayList; import java.util.Collections; @@ -88,11 +88,11 @@ public void testNonEmptyResponse() { /** * given: "We have a VMScheduleManager and ListVMScheduleCmd" * when: "ListVMScheduleCmd is executed with an invalid parameter" - * then: "an InvalidParameterException is thrown" + * then: "an InvalidParameterValueException is thrown" */ - @Test(expected = InvalidParameterException.class) - public void testInvalidParameterException() { - Mockito.when(vmScheduleManager.listSchedule(listVMScheduleCmd)).thenThrow(InvalidParameterException.class); + @Test(expected = InvalidParameterValueException.class) + public void testInvalidParameterValueException() { + Mockito.when(vmScheduleManager.listSchedule(listVMScheduleCmd)).thenThrow(InvalidParameterValueException.class); listVMScheduleCmd.execute(); ListResponse actualResponseObject = (ListResponse) listVMScheduleCmd.getResponseObject(); } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/vm/ListVMsCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/ListVMsCmdTest.java new file mode 100644 index 000000000000..48b41a47fff3 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/ListVMsCmdTest.java @@ -0,0 +1,223 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.vm; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.anyList; +import static org.mockito.Mockito.anySet; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.response.ResourceIconResponse; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import com.cloud.server.ResourceIcon; +import com.cloud.server.ResourceIconManager; +import com.cloud.server.ResourceTag; +import com.cloud.storage.GuestOS; +import com.cloud.utils.db.EntityManager; + +public class ListVMsCmdTest { + + EntityManager _entityMgr; + ResourceIconManager resourceIconManager; + ResponseGenerator _responseGenerator; + + ListVMsCmd cmd; + + @Before + public void setup() { + _entityMgr = mock(EntityManager.class); + resourceIconManager = mock(ResourceIconManager.class); + _responseGenerator = mock(ResponseGenerator.class); + cmd = spy(ListVMsCmd.class); + cmd._entityMgr = _entityMgr; + cmd.resourceIconManager = resourceIconManager; + cmd._responseGenerator = _responseGenerator; + } + + @Test + public void testUpdateVMResponse_withMixedIcons() { + String vm1Uuid = UUID.randomUUID().toString(); + UserVmResponse vm1 = mock(UserVmResponse.class); + when(vm1.getId()).thenReturn(vm1Uuid); + String vm2Uuid = UUID.randomUUID().toString(); + UserVmResponse vm2 = mock(UserVmResponse.class); + when(vm2.getId()).thenReturn(vm2Uuid); + List responses = Arrays.asList(vm1, vm2); + ResourceIcon icon1 = mock(ResourceIcon.class); + ResourceIcon icon2 = mock(ResourceIcon.class); + Map initialIcons = new HashMap<>(); + initialIcons.put(vm1Uuid, icon1); + when(resourceIconManager.getByResourceTypeAndUuids(ResourceTag.ResourceObjectType.UserVm, Set.of(vm1Uuid, vm2Uuid))) + .thenReturn(initialIcons); + Map fallbackIcons = Map.of(vm2Uuid, icon2); + doReturn(fallbackIcons).when(cmd).getResourceIconsForUsingTemplateIso(anyList()); + ResourceIconResponse iconResponse1 = new ResourceIconResponse(); + ResourceIconResponse iconResponse2 = new ResourceIconResponse(); + when(_responseGenerator.createResourceIconResponse(icon1)).thenReturn(iconResponse1); + when(_responseGenerator.createResourceIconResponse(icon2)).thenReturn(iconResponse2); + cmd.updateVMResponse(responses); + verify(vm1).setResourceIconResponse(iconResponse1); + verify(vm2).setResourceIconResponse(iconResponse2); + } + + @Test + public void testUpdateVMResponse_withEmptyList() { + cmd.updateVMResponse(Collections.emptyList()); + verify(resourceIconManager, never()).getByResourceTypeAndIds(Mockito.any(), Mockito.anyCollection()); + } + + @Test + public void testGetResourceIconsForUsingTemplateIso_withValidData() { + String vm1Uuid = UUID.randomUUID().toString(); + String template1Uuid = UUID.randomUUID().toString(); + UserVmResponse vm1 = mock(UserVmResponse.class); + when(vm1.getId()).thenReturn(vm1Uuid); + when(vm1.getTemplateId()).thenReturn(template1Uuid); + when(vm1.getIsoId()).thenReturn(null); + String vm2Uuid = UUID.randomUUID().toString(); + String iso2Uuid = UUID.randomUUID().toString(); + UserVmResponse vm2 = mock(UserVmResponse.class); + when(vm2.getId()).thenReturn(vm2Uuid); + when(vm2.getTemplateId()).thenReturn(null); + when(vm2.getIsoId()).thenReturn(iso2Uuid); + List responses = Arrays.asList(vm1, vm2); + Map templateIcons = new HashMap<>(); + templateIcons.put(template1Uuid, mock(ResourceIcon.class)); + Map isoIcons = new HashMap<>(); + isoIcons.put(iso2Uuid, mock(ResourceIcon.class)); + when(resourceIconManager.getByResourceTypeAndUuids(ResourceTag.ResourceObjectType.Template, Set.of(template1Uuid))) + .thenReturn(templateIcons); + when(resourceIconManager.getByResourceTypeAndUuids(ResourceTag.ResourceObjectType.ISO, Set.of(iso2Uuid))) + .thenReturn(isoIcons); + doReturn(Collections.emptyMap()).when(cmd).getResourceIconsUsingOsCategory(anyList()); + Map result = cmd.getResourceIconsForUsingTemplateIso(responses); + assertEquals(2, result.size()); + assertTrue(result.containsKey(vm1Uuid)); + assertTrue(result.containsKey(vm2Uuid)); + assertEquals(templateIcons.get(template1Uuid), result.get(vm1Uuid)); + assertEquals(isoIcons.get(iso2Uuid), result.get(vm2Uuid)); + } + + @Test + public void testGetResourceIconsForUsingTemplateIso_withMissingIcons() { + String vm1Uuid = UUID.randomUUID().toString(); + String template1Uuid = UUID.randomUUID().toString(); + UserVmResponse vm1 = mock(UserVmResponse.class); + when(vm1.getId()).thenReturn(vm1Uuid); + when(vm1.getTemplateId()).thenReturn(template1Uuid); + when(vm1.getIsoId()).thenReturn(null); + List responses = List.of(vm1); + when(resourceIconManager.getByResourceTypeAndUuids(eq(ResourceTag.ResourceObjectType.Template), anySet())) + .thenReturn(Collections.emptyMap()); + when(resourceIconManager.getByResourceTypeAndUuids(eq(ResourceTag.ResourceObjectType.ISO), anySet())) + .thenReturn(Collections.emptyMap()); + Map fallbackIcons = Map.of(vm1Uuid, mock(ResourceIcon.class)); + doReturn(fallbackIcons).when(cmd).getResourceIconsUsingOsCategory(anyList()); + Map result = cmd.getResourceIconsForUsingTemplateIso(responses); + assertEquals(1, result.size()); + assertEquals(fallbackIcons.get("vm1"), result.get("vm1")); + } + + @Test + public void testGetResourceIconsUsingOsCategory_withValidData() { + String vm1Uuid = UUID.randomUUID().toString(); + String os1Uuid = UUID.randomUUID().toString(); + UserVmResponse vm1 = mock(UserVmResponse.class); + when(vm1.getGuestOsId()).thenReturn(os1Uuid); + when(vm1.getId()).thenReturn(vm1Uuid); + String vm2Uuid = UUID.randomUUID().toString(); + String os2Uuid = UUID.randomUUID().toString(); + UserVmResponse vm2 = mock(UserVmResponse.class); + when(vm2.getGuestOsId()).thenReturn(os2Uuid); + when(vm2.getId()).thenReturn(vm2Uuid); + List responses = Arrays.asList(vm1, vm2); + GuestOS guestOS1 = mock(GuestOS.class); + when(guestOS1.getUuid()).thenReturn(os1Uuid); + when(guestOS1.getCategoryId()).thenReturn(10L); + GuestOS guestOS2 = mock(GuestOS.class); + when(guestOS2.getUuid()).thenReturn(os2Uuid); + when(guestOS2.getCategoryId()).thenReturn(20L); + when(_entityMgr.listByUuids(eq(GuestOS.class), anySet())) + .thenReturn(Arrays.asList(guestOS1, guestOS2)); + ResourceIcon icon1 = mock(ResourceIcon.class); + ResourceIcon icon2 = mock(ResourceIcon.class); + Map categoryIcons = new HashMap<>(); + categoryIcons.put(10L, icon1); + categoryIcons.put(20L, icon2); + when(resourceIconManager.getByResourceTypeAndIds(eq(ResourceTag.ResourceObjectType.GuestOsCategory), anySet())) + .thenReturn(categoryIcons); + Map result = cmd.getResourceIconsUsingOsCategory(responses); + assertEquals(2, result.size()); + assertEquals(icon1, result.get(vm1Uuid)); + assertEquals(icon2, result.get(vm2Uuid)); + } + + @Test + public void testGetResourceIconsUsingOsCategory_missingGuestOS() { + String vm1Uuid = UUID.randomUUID().toString(); + String os1Uuid = UUID.randomUUID().toString(); + UserVmResponse vm1 = mock(UserVmResponse.class); + when(vm1.getGuestOsId()).thenReturn(vm1Uuid); + when(vm1.getId()).thenReturn(os1Uuid); + List responses = Collections.singletonList(vm1); + when(_entityMgr.listByUuids(eq(GuestOS.class), anySet())) + .thenReturn(Collections.emptyList()); + Map result = cmd.getResourceIconsUsingOsCategory(responses); + assertTrue(result.isEmpty()); + } + + @Test + public void testGetResourceIconsUsingOsCategory_missingIcon() { + UserVmResponse vm1 = mock(UserVmResponse.class); + String vmUuid = UUID.randomUUID().toString(); + String osUuid = UUID.randomUUID().toString(); + when(vm1.getGuestOsId()).thenReturn(osUuid); + when(vm1.getId()).thenReturn(vmUuid); + List responses = Collections.singletonList(vm1); + GuestOS guestOS1 = mock(GuestOS.class); + when(guestOS1.getCategoryId()).thenReturn(10L); + when(guestOS1.getUuid()).thenReturn(osUuid); + when(_entityMgr.listByUuids(eq(GuestOS.class), anySet())) + .thenReturn(Collections.singletonList(guestOS1)); + when(resourceIconManager.getByResourceTypeAndIds(eq(ResourceTag.ResourceObjectType.GuestOsCategory), anySet())) + .thenReturn(Collections.emptyMap()); + Map result = cmd.getResourceIconsUsingOsCategory(responses); + assertTrue(result.containsKey(vmUuid)); + assertNull(result.get(vmUuid)); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/vm/UpdateVMScheduleCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/UpdateVMScheduleCmdTest.java index 5ce133382f3e..2c6c485f25bf 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 @@ -33,8 +33,6 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import java.security.InvalidParameterException; - public class UpdateVMScheduleCmdTest { @Mock public VMScheduleManager vmScheduleManager; @@ -71,11 +69,11 @@ public void testSuccessfulExecution() { /** * given: "We have a VMScheduleManager and UpdateVMScheduleCmd" * when: "UpdateVMScheduleCmd is executed with an invalid parameter" - * then: "an InvalidParameterException is thrown" + * then: "an InvalidParameterValueException is thrown" */ - @Test(expected = InvalidParameterException.class) - public void testInvalidParameterException() { - Mockito.when(vmScheduleManager.updateSchedule(updateVMScheduleCmd)).thenThrow(InvalidParameterException.class); + @Test(expected = InvalidParameterValueException.class) + public void testInvalidParameterValueException() { + Mockito.when(vmScheduleManager.updateSchedule(updateVMScheduleCmd)).thenThrow(InvalidParameterValueException.class); updateVMScheduleCmd.execute(); } @@ -99,7 +97,7 @@ public void testSuccessfulGetEntityOwnerId() { /** * given: "We have an EntityManager and UpdateVMScheduleCmd" * when: "UpdateVMScheduleCmd.getEntityOwnerId is executed for a VM Schedule which doesn't exist" - * then: "an InvalidParameterException is thrown" + * then: "an InvalidParameterValueException is thrown" */ @Test(expected = InvalidParameterValueException.class) public void testFailureGetEntityOwnerId() { diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/volume/CheckAndRepairVolumeCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/volume/CheckAndRepairVolumeCmdTest.java new file mode 100644 index 000000000000..226ff882fa7b --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/volume/CheckAndRepairVolumeCmdTest.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.volume; + +import com.cloud.exception.InvalidParameterValueException; +import junit.framework.TestCase; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class CheckAndRepairVolumeCmdTest extends TestCase { + private CheckAndRepairVolumeCmd checkAndRepairVolumeCmd; + private AutoCloseable closeable; + + @Before + public void setup() { + closeable = MockitoAnnotations.openMocks(this); + checkAndRepairVolumeCmd = new CheckAndRepairVolumeCmd(); + } + + @After + public void tearDown() throws Exception { + closeable.close(); + } + + @Test + public void testGetRepair() { + ReflectionTestUtils.setField(checkAndRepairVolumeCmd, "repair", "all"); + assertEquals("all", checkAndRepairVolumeCmd.getRepair()); + + ReflectionTestUtils.setField(checkAndRepairVolumeCmd, "repair", "LEAKS"); + assertEquals("leaks", checkAndRepairVolumeCmd.getRepair()); + + ReflectionTestUtils.setField(checkAndRepairVolumeCmd, "repair", null); + assertNull(checkAndRepairVolumeCmd.getRepair()); + } + + @Test(expected = InvalidParameterValueException.class) + public void testGetRepairInvalid() { + ReflectionTestUtils.setField(checkAndRepairVolumeCmd, "repair", "RANDOM STRING"); + checkAndRepairVolumeCmd.getRepair(); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmdTest.java index 79f27fd6687e..18c03f8f4bb6 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmdTest.java @@ -25,7 +25,6 @@ import com.cloud.network.vpc.VpcService; import com.cloud.user.AccountService; import com.cloud.utils.db.EntityManager; -import junit.framework.TestCase; import org.apache.cloudstack.api.ResponseGenerator; import org.apache.cloudstack.api.ResponseObject; import org.apache.cloudstack.api.response.VpcResponse; @@ -39,7 +38,7 @@ import org.springframework.test.util.ReflectionTestUtils; @RunWith(MockitoJUnitRunner.class) -public class CreateVPCCmdTest extends TestCase { +public class CreateVPCCmdTest { @Mock public VpcService _vpcService; @@ -47,9 +46,13 @@ public class CreateVPCCmdTest extends TestCase { public EntityManager _entityMgr; @Mock public AccountService _accountService; - private ResponseGenerator responseGenerator; + @Mock + private ResponseGenerator _responseGenerator; + @Mock + private Object job; + @InjectMocks - CreateVPCCmd cmd = new CreateVPCCmd(); + CreateVPCCmd cmd; @Test public void testGetAccountName() { @@ -86,6 +89,20 @@ public void testGetCidr() { Assert.assertEquals(cmd.getCidr(), cidr); } + @Test + public void testGetCidrSize() { + int cidrSize = 24; + ReflectionTestUtils.setField(cmd, "cidrSize", cidrSize); + Assert.assertEquals(cidrSize, (int) cmd.getCidrSize()); + } + + @Test + public void testAsNumber() { + long asNumber = 10000; + ReflectionTestUtils.setField(cmd, "asNumber", asNumber); + Assert.assertEquals(asNumber, (long) cmd.getAsNumber()); + } + @Test public void testGetDisplayText() { String displayText = "VPC Network"; @@ -167,17 +184,14 @@ public void testCreate() throws ResourceAllocationException { @Test public void testExecute() throws ResourceUnavailableException, InsufficientCapacityException { - ReflectionTestUtils.setField(cmd, "start", true); Vpc vpc = Mockito.mock(Vpc.class); VpcResponse response = Mockito.mock(VpcResponse.class); ReflectionTestUtils.setField(cmd, "id", 1L); - responseGenerator = Mockito.mock(ResponseGenerator.class); - Mockito.when(_vpcService.startVpc(1L, true)).thenReturn(true); + Mockito.doNothing().when(_vpcService).startVpc(cmd); Mockito.when(_entityMgr.findById(Mockito.eq(Vpc.class), Mockito.any(Long.class))).thenReturn(vpc); - cmd._responseGenerator = responseGenerator; - Mockito.when(responseGenerator.createVpcResponse(ResponseObject.ResponseView.Restricted, vpc)).thenReturn(response); + Mockito.when(_responseGenerator.createVpcResponse(ResponseObject.ResponseView.Restricted, vpc)).thenReturn(response); cmd.execute(); - Mockito.verify(_vpcService, Mockito.times(1)).startVpc(Mockito.anyLong(), Mockito.anyBoolean()); + Mockito.verify(_vpcService, Mockito.times(1)).startVpc(cmd); } } diff --git a/api/src/test/java/org/apache/cloudstack/api/response/ASNRangeResponseTest.java b/api/src/test/java/org/apache/cloudstack/api/response/ASNRangeResponseTest.java new file mode 100644 index 000000000000..50248383b4f1 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/response/ASNRangeResponseTest.java @@ -0,0 +1,51 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.response; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Date; + +@RunWith(MockitoJUnitRunner.class) +public final class ASNRangeResponseTest { + + private static String uuid = "uuid"; + private static String zoneId = "zoneid"; + private static long startASNumber = 10; + private static long endASNumber = 20; + private static Date created = new Date(); + + @Test + public void testASNRangeResponse() { + final ASNRangeResponse response = new ASNRangeResponse(); + + response.setId(uuid); + response.setZoneId(zoneId); + response.setStartASNumber(startASNumber); + response.setEndASNumber(endASNumber); + response.setCreated(created); + + Assert.assertEquals(uuid, response.getId()); + Assert.assertEquals(zoneId, response.getZoneId()); + Assert.assertEquals(startASNumber, (long) response.getStartASNumber()); + Assert.assertEquals(endASNumber, (long) response.getEndASNumber()); + Assert.assertEquals(created, response.getCreated()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/response/ASNumberResponseTest.java b/api/src/test/java/org/apache/cloudstack/api/response/ASNumberResponseTest.java new file mode 100644 index 000000000000..9515984134eb --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/response/ASNumberResponseTest.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.response; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Date; + +@RunWith(MockitoJUnitRunner.class) +public final class ASNumberResponseTest { + + private static String uuid = "uuid"; + private static String accountId = "account-id"; + private static String accountName = "account-name"; + private static String domainId = "domain-uuid"; + private static String domainName = "domain-name"; + private static Long asNumber = 15000L; + private static String asNumberRangeId = "as-number-range-uuid"; + private static String asNumberRange = "10000-20000"; + private static String zoneId = "zone-id"; + private static String zoneName = "zone-name"; + private static Date allocated = new Date(); + private static String allocationState = "allocated"; + + private static String associatedNetworkId = "network-id"; + + private static String associatedNetworkName = "network-name"; + + private static String vpcId = "vpc-uuid"; + private static String vpcName = "vpc-name"; + private static Date created = new Date(); + + + + @Test + public void testASNumberResponse() { + final ASNumberResponse response = new ASNumberResponse(); + + response.setId(uuid); + response.setAccountId(accountId); + response.setAccountName(accountName); + response.setDomainId(domainId); + response.setDomainName(domainName); + response.setAsNumber(asNumber); + response.setAsNumberRangeId(asNumberRangeId); + response.setAsNumberRange(asNumberRange); + response.setZoneId(zoneId); + response.setZoneName(zoneName); + response.setAllocated(allocated); + response.setAllocationState(allocationState); + response.setAssociatedNetworkId(associatedNetworkId); + response.setAssociatedNetworkName(associatedNetworkName); + response.setVpcId(vpcId); + response.setVpcName(vpcName); + response.setCreated(created); + + Assert.assertEquals(uuid, response.getId()); + Assert.assertEquals(accountId, response.getAccountId()); + Assert.assertEquals(accountName, response.getAccountName()); + Assert.assertEquals(domainId, response.getDomainId()); + Assert.assertEquals(domainName, response.getDomainName()); + Assert.assertEquals(asNumber, response.getAsNumber()); + Assert.assertEquals(asNumberRangeId, response.getAsNumberRangeId()); + Assert.assertEquals(asNumberRange, response.getAsNumberRange()); + Assert.assertEquals(zoneId, response.getZoneId()); + Assert.assertEquals(zoneName, response.getZoneName()); + Assert.assertEquals(allocated, response.getAllocated()); + Assert.assertEquals(allocationState, response.getAllocationState()); + Assert.assertEquals(associatedNetworkId, response.getAssociatedNetworkId()); + Assert.assertEquals(associatedNetworkName, response.getAssociatedNetworkName()); + Assert.assertEquals(vpcId, response.getVpcId()); + Assert.assertEquals(vpcName, response.getVpcName()); + Assert.assertEquals(created, response.getCreated()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/response/BgpPeerResponseTest.java b/api/src/test/java/org/apache/cloudstack/api/response/BgpPeerResponseTest.java new file mode 100644 index 000000000000..7c82eb843685 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/response/BgpPeerResponseTest.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.response; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +@RunWith(MockitoJUnitRunner.class) +public final class BgpPeerResponseTest { + + private static String uuid = "uuid"; + private static String ip4Address = "ip4-address"; + private static String ip6Address = "ip6-address"; + private static Long asNumber = 15000L; + private static String password = "password"; + private static String accountName = "account-name"; + private static String domainId = "domain-uuid"; + private static String domainName = "domain-name"; + private static String projectId = "project-uuid"; + private static String projectName = "project-name"; + private static String zoneId = "zone-id"; + private static String zoneName = "zone-name"; + private static Date created = new Date(); + + @Test + public void testBgpPeerResponse() { + final BgpPeerResponse response = new BgpPeerResponse(); + + response.setId(uuid); + response.setIp4Address(ip4Address); + response.setIp6Address(ip6Address); + response.setAsNumber(asNumber); + response.setPassword(password); + response.setAccountName(accountName); + response.setDomainId(domainId); + response.setDomainName(domainName); + response.setProjectId(projectId); + response.setProjectName(projectName); + response.setZoneId(zoneId); + response.setZoneName(zoneName); + response.setCreated(created); + Map details = new HashMap<>(); + details.put("key", "value"); + response.setDetails(details); + + Assert.assertEquals(uuid, response.getId()); + Assert.assertEquals(ip4Address, response.getIp4Address()); + Assert.assertEquals(ip6Address, response.getIp6Address()); + Assert.assertEquals(asNumber, response.getAsNumber()); + Assert.assertEquals(password, response.getPassword()); + Assert.assertEquals(accountName, response.getAccountName()); + Assert.assertEquals(domainId, response.getDomainId()); + Assert.assertEquals(domainName, response.getDomainName()); + Assert.assertEquals(projectId, response.getProjectId()); + Assert.assertEquals(projectName, response.getProjectName()); + Assert.assertEquals(zoneId, response.getZoneId()); + Assert.assertEquals(zoneName, response.getZoneName()); + Assert.assertEquals(created, response.getCreated()); + Assert.assertEquals(details, response.getDetails()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/response/DataCenterIpv4SubnetResponseTest.java b/api/src/test/java/org/apache/cloudstack/api/response/DataCenterIpv4SubnetResponseTest.java new file mode 100644 index 000000000000..add9544de7d3 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/response/DataCenterIpv4SubnetResponseTest.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.api.response; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Date; + +@RunWith(MockitoJUnitRunner.class) +public final class DataCenterIpv4SubnetResponseTest { + + private static String uuid = "uuid"; + private static String subnet = "10.10.10.0/26"; + private static String accountName = "account-name"; + private static String domainId = "domain-uuid"; + private static String domainName = "domain-name"; + private static String projectId = "project-uuid"; + private static String projectName = "project-name"; + private static String zoneId = "zone-id"; + private static String zoneName = "zone-name"; + private static Date created = new Date(); + + @Test + public void testDataCenterIpv4SubnetResponse() { + final DataCenterIpv4SubnetResponse response = new DataCenterIpv4SubnetResponse(); + + response.setId(uuid); + response.setSubnet(subnet); + response.setAccountName(accountName); + response.setDomainId(domainId); + response.setDomainName(domainName); + response.setProjectId(projectId); + response.setProjectName(projectName); + response.setZoneId(zoneId); + response.setZoneName(zoneName); + response.setCreated(created); + + Assert.assertEquals(uuid, response.getId()); + Assert.assertEquals(subnet, response.getSubnet()); + Assert.assertEquals(accountName, response.getAccountName()); + Assert.assertEquals(domainId, response.getDomainId()); + Assert.assertEquals(domainName, response.getDomainName()); + Assert.assertEquals(projectId, response.getProjectId()); + Assert.assertEquals(projectName, response.getProjectName()); + Assert.assertEquals(zoneId, response.getZoneId()); + Assert.assertEquals(zoneName, response.getZoneName()); + Assert.assertEquals(created, response.getCreated()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/response/HostResponseTest.java b/api/src/test/java/org/apache/cloudstack/api/response/HostResponseTest.java index 523b3de9e3cf..04e3ad7df603 100644 --- a/api/src/test/java/org/apache/cloudstack/api/response/HostResponseTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/response/HostResponseTest.java @@ -23,6 +23,8 @@ import java.util.HashMap; import java.util.Map; +import com.cloud.hypervisor.Hypervisor; + public final class HostResponseTest extends TestCase { private static final String VALID_KEY = "validkey"; @@ -32,7 +34,7 @@ public final class HostResponseTest extends TestCase { public void testSetDetailsNull() { final HostResponse hostResponse = new HostResponse(); - hostResponse.setDetails(null); + hostResponse.setDetails(null, null); assertEquals(null, hostResponse.getDetails()); @@ -51,7 +53,7 @@ public void testSetDetailsWithRootCredentials() { final Map expectedDetails = new HashedMap(); expectedDetails.put(VALID_KEY, VALID_VALUE); - hostResponse.setDetails(details); + hostResponse.setDetails(details, Hypervisor.HypervisorType.KVM); final Map actualDetails = hostResponse.getDetails(); assertTrue(details != actualDetails); @@ -70,7 +72,7 @@ public void testSetDetailsWithoutRootCredentials() { final Map expectedDetails = new HashedMap(); expectedDetails.put(VALID_KEY, VALID_VALUE); - hostResponse.setDetails(details); + hostResponse.setDetails(details, Hypervisor.HypervisorType.KVM); final Map actualDetails = hostResponse.getDetails(); assertTrue(details != actualDetails); diff --git a/api/src/test/java/org/apache/cloudstack/api/response/Ipv4RouteResponseTest.java b/api/src/test/java/org/apache/cloudstack/api/response/Ipv4RouteResponseTest.java new file mode 100644 index 000000000000..717668d054e6 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/response/Ipv4RouteResponseTest.java @@ -0,0 +1,48 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.response; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public final class Ipv4RouteResponseTest { + + private static String subnet = "10.10.10.0/24"; + private static String gateway = "10.10.10.1"; + + @Test + public void testIpv4RouteResponse() { + final Ipv4RouteResponse response = new Ipv4RouteResponse(subnet, gateway); + + Assert.assertEquals(subnet, response.getSubnet()); + Assert.assertEquals(gateway, response.getGateway()); + } + + @Test + public void testIpv4RouteResponse2() { + final Ipv4RouteResponse response = new Ipv4RouteResponse(); + + response.setSubnet(subnet); + response.setGateway(gateway); + + Assert.assertEquals(subnet, response.getSubnet()); + Assert.assertEquals(gateway, response.getGateway()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/response/Ipv4SubnetForGuestNetworkResponseTest.java b/api/src/test/java/org/apache/cloudstack/api/response/Ipv4SubnetForGuestNetworkResponseTest.java new file mode 100644 index 000000000000..6fb5141e7a98 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/response/Ipv4SubnetForGuestNetworkResponseTest.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.response; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Date; + +@RunWith(MockitoJUnitRunner.class) +public final class Ipv4SubnetForGuestNetworkResponseTest { + + private static String uuid = "uuid"; + private static String parentId = "parent-id"; + private static String parentSubnet = "10.10.0.0/20"; + private static String subnet = "10.10.0.0/24"; + private static String state = "Allocating"; + + private static String zoneId = "zone-id"; + private static String zoneName = "zone-name"; + private static Date allocated = new Date(); + private static String networkId = "network-id"; + private static String networkName = "network-name"; + private static String vpcId = "vpc-uuid"; + private static String vpcName = "vpc-name"; + private static Date created = new Date(); + private static Date removed = new Date(); + + + + @Test + public void testIpv4SubnetForGuestNetworkResponse() { + final Ipv4SubnetForGuestNetworkResponse response = new Ipv4SubnetForGuestNetworkResponse(); + + response.setId(uuid); + response.setSubnet(subnet); + response.setParentId(parentId); + response.setParentSubnet(parentSubnet); + response.setState(state); + response.setZoneId(zoneId); + response.setZoneName(zoneName); + response.setAllocatedTime(allocated); + response.setNetworkId(networkId); + response.setNetworkName(networkName); + response.setVpcId(vpcId); + response.setVpcName(vpcName); + response.setCreated(created); + response.setRemoved(removed); + + Assert.assertEquals(uuid, response.getId()); + Assert.assertEquals(subnet, response.getSubnet()); + Assert.assertEquals(parentId, response.getParentId()); + Assert.assertEquals(parentSubnet, response.getParentSubnet()); + Assert.assertEquals(state, response.getState()); + Assert.assertEquals(zoneId, response.getZoneId()); + Assert.assertEquals(zoneName, response.getZoneName()); + Assert.assertEquals(allocated, response.getAllocatedTime()); + Assert.assertEquals(networkId, response.getNetworkId()); + Assert.assertEquals(networkName, response.getNetworkName()); + Assert.assertEquals(vpcId, response.getVpcId()); + Assert.assertEquals(vpcName, response.getVpcName()); + Assert.assertEquals(created, response.getCreated()); + Assert.assertEquals(removed, response.getRemoved()); + } +} 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/api/response/VolumeForImportResponseTest.java b/api/src/test/java/org/apache/cloudstack/api/response/VolumeForImportResponseTest.java new file mode 100644 index 000000000000..7d0538836bff --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/response/VolumeForImportResponseTest.java @@ -0,0 +1,77 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.response; + +import com.cloud.hypervisor.Hypervisor; +import com.cloud.storage.Storage; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.HashMap; +import java.util.Map; + +@RunWith(MockitoJUnitRunner.class) +public final class VolumeForImportResponseTest { + + private static String path = "path"; + private static String name = "name"; + private static String fullPath = "fullPath"; + private static String format = "qcow2"; + private static long size = 10; + private static long virtualSize = 20; + private static String encryptFormat = "LUKS"; + private static Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM; + private static String storagePoolId = "storage pool uuid"; + private static String storagePoolName = "storage pool 1"; + private static String storagePoolType = Storage.StoragePoolType.NetworkFilesystem.name(); + private static String chainInfo = "chain info"; + + @Test + public void testVolumeForImportResponse() { + final VolumeForImportResponse response = new VolumeForImportResponse(); + + response.setPath(path); + response.setName(name); + response.setFullPath(fullPath); + response.setFormat(format); + response.setSize(size); + response.setVirtualSize(virtualSize); + response.setQemuEncryptFormat(encryptFormat); + response.setStoragePoolType(storagePoolType); + response.setStoragePoolName(storagePoolName); + response.setStoragePoolId(storagePoolId); + response.setChainInfo(chainInfo); + Map details = new HashMap<>(); + details.put("key", "value"); + response.setDetails(details); + + Assert.assertEquals(path, response.getPath()); + Assert.assertEquals(name, response.getName()); + Assert.assertEquals(fullPath, response.getFullPath()); + Assert.assertEquals(format, response.getFormat()); + Assert.assertEquals(size, response.getSize()); + Assert.assertEquals(virtualSize, response.getVirtualSize()); + Assert.assertEquals(encryptFormat, response.getQemuEncryptFormat()); + Assert.assertEquals(storagePoolType, response.getStoragePoolType()); + Assert.assertEquals(storagePoolName, response.getStoragePoolName()); + Assert.assertEquals(storagePoolId, response.getStoragePoolId()); + Assert.assertEquals(chainInfo, response.getChainInfo()); + Assert.assertEquals(details, response.getDetails()); + } +} 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/extension/ExtensionCustomActionTest.java b/api/src/test/java/org/apache/cloudstack/extension/ExtensionCustomActionTest.java new file mode 100644 index 000000000000..ae4314aa11a8 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/extension/ExtensionCustomActionTest.java @@ -0,0 +1,484 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//with the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +//KIND, either express or implied. See the License for the +//specific language governing permissions and limitations +//under the License. + +package org.apache.cloudstack.extension; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.cloudstack.api.ApiConstants; +import org.junit.Test; + +import com.cloud.exception.InvalidParameterValueException; + +public class ExtensionCustomActionTest { + + @Test + public void testResourceType() { + ExtensionCustomAction.ResourceType vmType = ExtensionCustomAction.ResourceType.VirtualMachine; + assertEquals(com.cloud.vm.VirtualMachine.class, vmType.getAssociatedClass()); + } + + @Test + public void testParameterTypeSupportsOptions() { + assertTrue(ExtensionCustomAction.Parameter.Type.STRING.canSupportsOptions()); + assertTrue(ExtensionCustomAction.Parameter.Type.NUMBER.canSupportsOptions()); + assertFalse(ExtensionCustomAction.Parameter.Type.BOOLEAN.canSupportsOptions()); + assertFalse(ExtensionCustomAction.Parameter.Type.DATE.canSupportsOptions()); + } + + @Test + public void testValidationFormatBaseType() { + assertEquals(ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.UUID.getBaseType()); + assertEquals(ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.EMAIL.getBaseType()); + assertEquals(ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.PASSWORD.getBaseType()); + assertEquals(ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.URL.getBaseType()); + assertEquals(ExtensionCustomAction.Parameter.Type.NUMBER, + ExtensionCustomAction.Parameter.ValidationFormat.DECIMAL.getBaseType()); + assertNull(ExtensionCustomAction.Parameter.ValidationFormat.NONE.getBaseType()); + } + + @Test + public void testParameterFromMapValid() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, "testParam"); + map.put(ApiConstants.TYPE, "STRING"); + map.put(ApiConstants.VALIDATION_FORMAT, "EMAIL"); + map.put(ApiConstants.REQUIRED, "true"); + map.put(ApiConstants.VALUE_OPTIONS, "test@example.com,another@test.com"); + + ExtensionCustomAction.Parameter param = ExtensionCustomAction.Parameter.fromMap(map); + + assertEquals("testParam", param.getName()); + assertEquals(ExtensionCustomAction.Parameter.Type.STRING, param.getType()); + assertEquals(ExtensionCustomAction.Parameter.ValidationFormat.EMAIL, param.getValidationFormat()); + assertTrue(param.isRequired()); + assertEquals(2, param.getValueOptions().size()); + assertTrue(param.getValueOptions().contains("test@example.com")); + assertTrue(param.getValueOptions().contains("another@test.com")); + } + + @Test(expected = InvalidParameterValueException.class) + public void testParameterFromMapEmptyName() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, ""); + map.put(ApiConstants.TYPE, "STRING"); + + ExtensionCustomAction.Parameter.fromMap(map); + } + + @Test(expected = InvalidParameterValueException.class) + public void testParameterFromMapNoType() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, "testParam"); + + ExtensionCustomAction.Parameter.fromMap(map); + } + + @Test(expected = InvalidParameterValueException.class) + public void testParameterFromMapInvalidType() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, "testParam"); + map.put(ApiConstants.TYPE, "INVALID_TYPE"); + + ExtensionCustomAction.Parameter.fromMap(map); + } + + @Test(expected = InvalidParameterValueException.class) + public void testParameterFromMapInvalidValidationFormat() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, "testParam"); + map.put(ApiConstants.TYPE, "STRING"); + map.put(ApiConstants.VALIDATION_FORMAT, "INVALID_FORMAT"); + + ExtensionCustomAction.Parameter.fromMap(map); + } + + @Test(expected = InvalidParameterValueException.class) + public void testParameterFromMapMismatchedTypeAndFormat() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, "testParam"); + map.put(ApiConstants.TYPE, "STRING"); + map.put(ApiConstants.VALIDATION_FORMAT, "DECIMAL"); + + ExtensionCustomAction.Parameter.fromMap(map); + } + + @Test + public void testParameterFromMapWithNumberOptions() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, "testParam"); + map.put(ApiConstants.TYPE, "NUMBER"); + map.put(ApiConstants.VALIDATION_FORMAT, "DECIMAL"); + map.put(ApiConstants.VALUE_OPTIONS, "1.5,2.7,3.0"); + + ExtensionCustomAction.Parameter param = ExtensionCustomAction.Parameter.fromMap(map); + + assertEquals(ExtensionCustomAction.Parameter.Type.NUMBER, param.getType()); + assertEquals(ExtensionCustomAction.Parameter.ValidationFormat.DECIMAL, param.getValidationFormat()); + assertEquals(3, param.getValueOptions().size()); + assertTrue(param.getValueOptions().contains(1.5f)); + assertTrue(param.getValueOptions().contains(2.7f)); + assertTrue(param.getValueOptions().contains(3.0f)); + } + + @Test(expected = InvalidParameterValueException.class) + public void testParameterFromMapInvalidNumberOptions() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, "testParam"); + map.put(ApiConstants.TYPE, "NUMBER"); + map.put(ApiConstants.VALUE_OPTIONS, "1.5,invalid,3.0"); + + ExtensionCustomAction.Parameter.fromMap(map); + } + + @Test(expected = InvalidParameterValueException.class) + public void testParameterFromMapInvalidEmailOptions() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, "testParam"); + map.put(ApiConstants.TYPE, "STRING"); + map.put(ApiConstants.VALIDATION_FORMAT, "EMAIL"); + map.put(ApiConstants.VALUE_OPTIONS, "valid@email.com,invalid-email"); + + ExtensionCustomAction.Parameter.fromMap(map); + } + + @Test + public void testValidatedValueString() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.EMAIL, + null, + false + ); + + Object result = param.validatedValue("test@example.com"); + assertEquals("test@example.com", result); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidatedValueInvalidEmail() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.EMAIL, + null, + false + ); + + param.validatedValue("invalid-email"); + } + + @Test + public void testValidatedValueUUID() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.UUID, + null, + false + ); + + String validUUID = "550e8400-e29b-41d4-a716-446655440000"; + Object result = param.validatedValue(validUUID); + assertEquals(validUUID, result); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidatedValueInvalidUUID() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.UUID, + null, + false + ); + + param.validatedValue("invalid-uuid"); + } + + @Test + public void testValidatedValueURL() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.URL, + null, + false + ); + + Object result = param.validatedValue("https://example.com"); + assertEquals("https://example.com", result); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidatedValueInvalidURL() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.URL, + null, + false + ); + + param.validatedValue("not-a-url"); + } + + @Test + public void testValidatedValuePassword() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.PASSWORD, + null, + false + ); + + Object result = param.validatedValue("mypassword"); + assertEquals("mypassword", result); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidatedValueEmptyPassword() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.PASSWORD, + null, + false + ); + + param.validatedValue(" "); + } + + @Test + public void testValidatedValueNumber() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.NUMBER, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, + null, + false + ); + + Object result = param.validatedValue("42"); + assertEquals(42, result); + } + + @Test + public void testValidatedValueDecimal() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.NUMBER, + ExtensionCustomAction.Parameter.ValidationFormat.DECIMAL, + null, + false + ); + + Object result = param.validatedValue("3.14"); + assertEquals(3.14f, result); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidatedValueInvalidNumber() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.NUMBER, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, + null, + false + ); + + param.validatedValue("not-a-number"); + } + + @Test + public void testValidatedValueBoolean() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.BOOLEAN, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, + null, + false + ); + + Object result = param.validatedValue("true"); + assertEquals(true, result); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidatedValueInvalidBoolean() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.BOOLEAN, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, + null, + false + ); + + Object result = param.validatedValue("maybe"); + } + + @Test + public void testValidatedValueWithOptions() { + List options = Arrays.asList("option1", "option2", "option3"); + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, + options, + false + ); + + Object result = param.validatedValue("option2"); + assertEquals("option2", result); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidatedValueNotInOptions() { + List options = Arrays.asList("option1", "option2", "option3"); + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, + options, + false + ); + + param.validatedValue("option4"); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidatedValueEmpty() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, + null, + false + ); + + param.validatedValue(""); + } + + @Test + public void testValidateParameterValues() { + List paramDefs = Arrays.asList( + new ExtensionCustomAction.Parameter("required1", ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, null, true), + new ExtensionCustomAction.Parameter("required2", ExtensionCustomAction.Parameter.Type.NUMBER, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, null, true), + new ExtensionCustomAction.Parameter("optional", ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, null, false) + ); + + Map suppliedValues = new HashMap<>(); + suppliedValues.put("required1", "value1"); + suppliedValues.put("required2", "42"); + suppliedValues.put("optional", "optionalValue"); + + Map result = ExtensionCustomAction.Parameter.validateParameterValues(paramDefs, suppliedValues); + + assertEquals("value1", result.get("required1")); + assertEquals(42, result.get("required2")); + assertEquals("optionalValue", result.get("optional")); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidateParameterValuesMissingRequired() { + List paramDefs = Arrays.asList( + new ExtensionCustomAction.Parameter("required1", ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, null, true) + ); + + Map suppliedValues = new HashMap<>(); + + ExtensionCustomAction.Parameter.validateParameterValues(paramDefs, suppliedValues); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidateParameterValuesEmptyRequired() { + List paramDefs = Arrays.asList( + new ExtensionCustomAction.Parameter("required1", ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, null, true) + ); + + Map suppliedValues = new HashMap<>(); + suppliedValues.put("required1", " "); + + ExtensionCustomAction.Parameter.validateParameterValues(paramDefs, suppliedValues); + } + + @Test + public void testValidateParameterValuesNullSupplied() { + List paramDefs = Arrays.asList( + new ExtensionCustomAction.Parameter("optional", ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, null, false) + ); + + Map result = ExtensionCustomAction.Parameter.validateParameterValues(paramDefs, null); + assertTrue(result.isEmpty()); + } + + @Test + public void testJsonSerializationDeserialization() { + List originalParams = Arrays.asList( + new ExtensionCustomAction.Parameter("param1", ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.EMAIL, Arrays.asList("test@example.com"), true), + new ExtensionCustomAction.Parameter("param2", ExtensionCustomAction.Parameter.Type.NUMBER, + ExtensionCustomAction.Parameter.ValidationFormat.DECIMAL, Arrays.asList(1.5f, 2.7f), false) + ); + + String json = ExtensionCustomAction.Parameter.toJsonFromList(originalParams); + List deserializedParams = ExtensionCustomAction.Parameter.toListFromJson(json); + + assertEquals(originalParams.size(), deserializedParams.size()); + assertEquals(originalParams.get(0).getName(), deserializedParams.get(0).getName()); + assertEquals(originalParams.get(0).getType(), deserializedParams.get(0).getType()); + assertEquals(originalParams.get(0).getValidationFormat(), deserializedParams.get(0).getValidationFormat()); + assertEquals(originalParams.get(0).isRequired(), deserializedParams.get(0).isRequired()); + assertEquals(originalParams.get(0).getValueOptions(), deserializedParams.get(0).getValueOptions()); + } + + @Test + public void testToString() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.EMAIL, + null, + true + ); + + String result = param.toString(); + assertTrue(result.contains("testParam")); + assertTrue(result.contains("STRING")); + assertTrue(result.contains("true")); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/network/BgpPeerTOTest.java b/api/src/test/java/org/apache/cloudstack/network/BgpPeerTOTest.java new file mode 100644 index 000000000000..2d1f8868ffc4 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/network/BgpPeerTOTest.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.network; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +public class BgpPeerTOTest { + + private static Long peerId = 100L; + private static String ip4Address = "ip4-address"; + private static String ip6Address = "ip6-address"; + private static Long peerAsNumber = 15000L; + private static String peerPassword = "peer-password"; + private static Long networkId = 200L; + private static Long networkAsNumber = 20000L; + private static String guestIp4Cidr = "10.10.10.0/24"; + private static String guestIp6Cidr = "fd00:1111:2222:3333::1/64"; + + @Test + public void testBgpPeerTO1() { + BgpPeerTO bgpPeerTO = new BgpPeerTO(networkId); + + Assert.assertEquals(networkId, bgpPeerTO.getNetworkId()); + } + + @Test + public void testBgpPeerTO2() { + Map details = new HashMap<>(); + details.put(BgpPeer.Detail.EBGP_MultiHop, "100"); + + BgpPeerTO bgpPeerTO = new BgpPeerTO(peerId, ip4Address, ip6Address, peerAsNumber, peerPassword, + networkId, networkAsNumber, guestIp4Cidr, guestIp6Cidr, details); + + Assert.assertEquals(peerId, bgpPeerTO.getPeerId()); + Assert.assertEquals(peerAsNumber, bgpPeerTO.getPeerAsNumber()); + Assert.assertEquals(ip4Address, bgpPeerTO.getIp4Address()); + Assert.assertEquals(ip6Address, bgpPeerTO.getIp6Address()); + Assert.assertEquals(peerPassword, bgpPeerTO.getPeerPassword()); + Assert.assertEquals(networkId, bgpPeerTO.getNetworkId()); + Assert.assertEquals(networkAsNumber, bgpPeerTO.getNetworkAsNumber()); + Assert.assertEquals(guestIp4Cidr, bgpPeerTO.getGuestIp4Cidr()); + Assert.assertEquals(guestIp6Cidr, bgpPeerTO.getGuestIp6Cidr()); + + Assert.assertNotNull(bgpPeerTO.getDetails()); + details = bgpPeerTO.getDetails(); + Assert.assertEquals(1, details.size()); + Assert.assertEquals("100", details.get(BgpPeer.Detail.EBGP_MultiHop)); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/storage/volume/VolumeOnStorageTOTest.java b/api/src/test/java/org/apache/cloudstack/storage/volume/VolumeOnStorageTOTest.java new file mode 100644 index 000000000000..59de3b8ac4ee --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/storage/volume/VolumeOnStorageTOTest.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.storage.volume; + +import com.cloud.hypervisor.Hypervisor; +import org.junit.Assert; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +public class VolumeOnStorageTOTest { + + private static String path = "path"; + private static String name = "name"; + private static String fullPath = "fullPath"; + private static String format = "qcow2"; + private static long size = 10; + private static long virtualSize = 20; + private static String encryptFormat = "LUKS"; + private static Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM; + private static String BACKING_FILE = "backing file"; + private static String BACKING_FILE_FORMAT = "qcow2"; + + @Test + public void testVolumeOnStorageTO() { + VolumeOnStorageTO volumeOnStorageTO = new VolumeOnStorageTO(hypervisorType, path, name, fullPath, + format, size, virtualSize); + + Assert.assertEquals(hypervisorType, volumeOnStorageTO.getHypervisorType()); + Assert.assertEquals(path, volumeOnStorageTO.getPath()); + Assert.assertEquals(name, volumeOnStorageTO.getName()); + Assert.assertEquals(fullPath, volumeOnStorageTO.getFullPath()); + Assert.assertEquals(format, volumeOnStorageTO.getFormat()); + Assert.assertEquals(size, volumeOnStorageTO.getSize()); + Assert.assertEquals(virtualSize, volumeOnStorageTO.getVirtualSize()); + } + + @Test + public void testVolumeOnStorageTO3() { + VolumeOnStorageTO volumeOnStorageTO = new VolumeOnStorageTO(); + volumeOnStorageTO.setHypervisorType(hypervisorType); + volumeOnStorageTO.setPath(path); + volumeOnStorageTO.setFullPath(fullPath); + volumeOnStorageTO.setName(name); + volumeOnStorageTO.setFormat(format); + volumeOnStorageTO.setSize(size); + volumeOnStorageTO.setVirtualSize(virtualSize); + volumeOnStorageTO.setQemuEncryptFormat(encryptFormat); + + Map details = new HashMap<>(); + details.put(VolumeOnStorageTO.Detail.BACKING_FILE, BACKING_FILE); + volumeOnStorageTO.setDetails(details); + volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.BACKING_FILE_FORMAT, BACKING_FILE_FORMAT); + + Assert.assertEquals(hypervisorType, volumeOnStorageTO.getHypervisorType()); + Assert.assertEquals(path, volumeOnStorageTO.getPath()); + Assert.assertEquals(name, volumeOnStorageTO.getName()); + Assert.assertEquals(fullPath, volumeOnStorageTO.getFullPath()); + Assert.assertEquals(format, volumeOnStorageTO.getFormat()); + Assert.assertEquals(size, volumeOnStorageTO.getSize()); + Assert.assertEquals(virtualSize, volumeOnStorageTO.getVirtualSize()); + Assert.assertEquals(encryptFormat, volumeOnStorageTO.getQemuEncryptFormat()); + + details = volumeOnStorageTO.getDetails(); + Assert.assertEquals(2, details.size()); + Assert.assertEquals(BACKING_FILE, details.get(VolumeOnStorageTO.Detail.BACKING_FILE)); + Assert.assertEquals(BACKING_FILE_FORMAT, details.get(VolumeOnStorageTO.Detail.BACKING_FILE_FORMAT)); + } +} diff --git a/build/replace.properties b/build/replace.properties index 3d9a45970600..8c3812eb7d29 100644 --- a/build/replace.properties +++ b/build/replace.properties @@ -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 @@ -28,3 +28,4 @@ MSMNTDIR=/mnt COMPONENTS-SPEC=components.xml REMOTEHOST=localhost COMMONLIBDIR=client/target/common/ +EXTENSIONSDEPLOYMENTMODE=developer diff --git a/client/bindir/cloud-setup-management.in b/client/bindir/cloud-setup-management.in index 70e727b40b29..b4fe76cc8d8a 100755 --- a/client/bindir/cloud-setup-management.in +++ b/client/bindir/cloud-setup-management.in @@ -16,13 +16,127 @@ # specific language governing permissions and limitations # under the License. +import os import sys +# ---- This snippet of code adds the sources path and the waf configured PYTHONDIR to the Python path ---- +# ---- We do this so cloud_utils can be looked up in the following order: +# ---- 1) Sources directory +# ---- 2) waf configured PYTHONDIR +# ---- 3) System Python path +for pythonpath in ( + "@PYTHONDIR@", + os.path.join(os.path.dirname(__file__),os.path.pardir,os.path.pardir,"python","lib"), + ): + if os.path.isdir(pythonpath): sys.path.insert(0,pythonpath) +# ---- End snippet of code ---- + from cloudutils.syscfg import sysConfigFactory from cloudutils.utilities import initLoging, UnknownSystemException from cloudutils.cloudException import CloudRuntimeException, CloudInternalException from cloudutils.globalEnv import globalEnv from cloudutils.serviceConfigServer import cloudManagementConfig from optparse import OptionParser +import urllib.request +import configparser +import hashlib + +SYSTEMVM_TEMPLATES_PATH = "/usr/share/cloudstack-management/templates/systemvm" +SYSTEMVM_TEMPLATES_METADATA_FILE = SYSTEMVM_TEMPLATES_PATH + "/metadata.ini" + +def verify_sha512_checksum(file_path, expected_checksum): + sha512 = hashlib.sha512() + try: + with open(file_path, "rb") as f: + for chunk in iter(lambda: f.read(8192), b""): + sha512.update(chunk) + return sha512.hexdigest().lower() == expected_checksum.lower() + except Exception as e: + print(f"Failed to verify checksum for {file_path}: {e}") + return False + +def download_file(url, dest_path, chunk_size=8 * 1024 * 1024): + """ + Downloads a file from the given URL to the specified destination path in chunks. + """ + try: + with urllib.request.urlopen(url) as response: + total_size = response.length if response.length else None + downloaded = 0 + try: + with open(dest_path, 'wb') as out_file: + while True: + chunk = response.read(chunk_size) + if not chunk: + break + out_file.write(chunk) + downloaded += len(chunk) + if total_size: + print(f"Downloaded {downloaded / (1024 * 1024):.2f}MB of {total_size / (1024 * 1024):.2f}MB", end='\r') + except PermissionError as pe: + print(f"Permission denied: {dest_path}") + raise + print(f"\nDownloaded file from {url} to {dest_path}") + except Exception as e: + print(f"Failed to download file: {e}") + raise + +def download_template_if_needed(template, url, filename, checksum): + dest_path = os.path.join(SYSTEMVM_TEMPLATES_PATH, filename) + if os.path.exists(dest_path): + if checksum and verify_sha512_checksum(dest_path, checksum): + print(f"{template} System VM template already exists at {dest_path} with valid checksum, skipping download.") + return + else: + print(f"{template} System VM template at {dest_path} has invalid or missing checksum, re-downloading...") + else: + print(f"Downloading {template} System VM template from {url} to {dest_path}...") + try: + download_file(url, dest_path) + #After download, verify checksum if provided + if checksum: + if verify_sha512_checksum(dest_path, checksum): + print(f"{template} System VM template downloaded and verified successfully.") + else: + print(f"ERROR: Checksum verification failed for {template} System VM template after download.") + except Exception as e: + print(f"ERROR: Failed to download {template} System VM template: {e}") + +def collect_template_metadata(selected_templates, options): + template_metadata_list = [] + if not os.path.exists(SYSTEMVM_TEMPLATES_METADATA_FILE): + print(f"ERROR: System VM templates metadata file not found at {SYSTEMVM_TEMPLATES_METADATA_FILE}, cannot download templates.") + sys.exit(1) + config = configparser.ConfigParser() + config.read(SYSTEMVM_TEMPLATES_METADATA_FILE) + template_repo_url = None + if options.systemvm_templates_repository: + if "default" in config and "downloadrepository" in config["default"]: + template_repo_url = config["default"]["downloadrepository"].strip() + if not template_repo_url: + print("ERROR: downloadrepository value is empty in metadata file, cannot use --systemvm-template-repository option.") + sys.exit(1) + for template in selected_templates: + if template in config: + url = config[template].get("downloadurl") + filename = config[template].get("filename") + checksum = config[template].get("checksum") + if url and filename: + if template_repo_url: + url = url.replace(template_repo_url, options.systemvm_templates_repository) + template_metadata_list.append({ + "template": template, + "url": url, + "filename": filename, + "checksum": checksum + }) + else: + print(f"ERROR: URL or filename not found for {template} System VM template in metadata.") + sys.exit(1) + else: + print(f"ERROR: No metadata found for {template} System VM template.") + sys.exit(1) + return template_metadata_list + if __name__ == '__main__': initLoging("@MSLOGDIR@/setupManagement.log") glbEnv = globalEnv() @@ -31,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" @@ -39,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:") @@ -60,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 9ce1898dc817..fca48f9eafbb 100755 --- a/client/bindir/cloud-update-xenserver-licenses.in +++ b/client/bindir/cloud-update-xenserver-licenses.in @@ -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 @@ -34,149 +34,191 @@ 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...> +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/db.properties.in b/client/conf/db.properties.in index 8f31aff17e63..0f7d2706a427 100644 --- a/client/conf/db.properties.in +++ b/client/conf/db.properties.in @@ -34,10 +34,14 @@ db.cloud.uri= # CloudStack database tuning parameters +db.cloud.connectionPoolLib=hikaricp db.cloud.maxActive=250 db.cloud.maxIdle=30 -db.cloud.maxWait=10000 -db.cloud.validationQuery=SELECT 1 +db.cloud.maxWait=600000 +db.cloud.minIdleConnections=5 +db.cloud.connectionTimeout=30000 +db.cloud.keepAliveTime=600000 +db.cloud.validationQuery=/* ping */ SELECT 1 db.cloud.testOnBorrow=true db.cloud.testWhileIdle=true db.cloud.timeBetweenEvictionRunsMillis=40000 @@ -70,9 +74,13 @@ db.usage.uri= # usage database tuning parameters +db.usage.connectionPoolLib=hikaricp db.usage.maxActive=100 db.usage.maxIdle=30 -db.usage.maxWait=10000 +db.usage.maxWait=600000 +db.usage.minIdleConnections=5 +db.usage.connectionTimeout=30000 +db.usage.keepAliveTime=600000 db.usage.url.params=serverTimezone=UTC # Simulator database settings @@ -82,9 +90,13 @@ db.simulator.host=@DBHOST@ db.simulator.driver=@DBDRIVER@ db.simulator.port=3306 db.simulator.name=simulator +db.simulator.connectionPoolLib=hikaricp db.simulator.maxActive=250 db.simulator.maxIdle=30 -db.simulator.maxWait=10000 +db.simulator.maxWait=600000 +db.simulator.minIdleConnections=5 +db.simulator.connectionTimeout=30000 +db.simulator.keepAliveTime=600000 db.simulator.autoReconnect=true # Connection URI to the database "simulator". When this property is set, only the following properties will be used along with it: db.simulator.host, db.simulator.port, db.simulator.name, db.simulator.autoReconnect. Other properties will be ignored. diff --git a/client/conf/log4j-cloud.xml.in b/client/conf/log4j-cloud.xml.in index dbcf8c6198bb..26da171269de 100755 --- a/client/conf/log4j-cloud.xml.in +++ b/client/conf/log4j-cloud.xml.in @@ -34,7 +34,7 @@ under the License. - + @@ -43,7 +43,7 @@ under the License. - + @@ -52,7 +52,7 @@ under the License. - + @@ -61,7 +61,7 @@ under the License. - + @@ -69,7 +69,7 @@ under the License. - + diff --git a/client/conf/server.properties.in b/client/conf/server.properties.in index 57d81c812178..7c5e3f925b08 100644 --- a/client/conf/server.properties.in +++ b/client/conf/server.properties.in @@ -32,6 +32,9 @@ session.timeout=30 # Max allowed API request payload/content size in bytes request.content.size=1048576 +# Max allowed API request form keys +request.max.form.keys=5000 + # Options to configure and enable HTTPS on the management server # # For the management server to pick up these configuration settings, the configured @@ -52,3 +55,15 @@ webapp.dir=/usr/share/cloudstack-management/webapp # The path to access log file access.log=/var/log/cloudstack/management/access.log + +# The deployment mode for the extensions +extensions.deployment.mode=@EXTENSIONSDEPLOYMENTMODE@ + +# Thread pool configuration +#threads.min=10 +#threads.max=500 + +# 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 91399097d648..b8dffe65d4fb 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -25,24 +25,9 @@ org.apache.cloudstack cloudstack - 4.20.0.0-SNAPSHOT + 4.23.0.0-SNAPSHOT - - - juniper-contrail - https://juniper.github.io/contrail-maven/snapshots - - - juniper-tungsten-api - https://github.com/radu-todirica/tungsten-api/raw/master - - - - net.juniper.tungsten - juniper-tungsten-api - 2.0 - javax.servlet javax.servlet-api @@ -68,8 +53,8 @@ jetty-util - mysql - mysql-connector-java + com.mysql + mysql-connector-j org.apache.cloudstack @@ -271,11 +256,6 @@ cloud-plugin-network-nvp ${project.version} - - org.apache.cloudstack - cloud-plugin-network-contrail - ${project.version} - org.apache.cloudstack cloud-plugin-network-palo-alto @@ -291,11 +271,6 @@ cloud-plugin-network-ovs ${project.version} - - org.apache.cloudstack - cloud-plugin-network-tungsten - ${project.version} - org.apache.cloudstack cloud-plugin-network-elb @@ -346,16 +321,6 @@ cloud-plugin-hypervisor-ucs ${project.version} - - org.apache.cloudstack - cloud-plugin-hypervisor-ovm - ${project.version} - - - org.apache.cloudstack - cloud-plugin-hypervisor-ovm3 - ${project.version} - org.apache.cloudstack cloud-plugin-hypervisor-kvm @@ -372,6 +337,11 @@ cloud-plugin-hypervisor-hyperv ${project.version} + + org.apache.cloudstack + cloud-plugin-hypervisor-external + ${project.version} + org.apache.cloudstack cloud-plugin-storage-allocator-random @@ -447,6 +417,11 @@ cloud-mom-kafka ${project.version} + + org.apache.cloudstack + cloud-mom-webhook + ${project.version} + org.apache.cloudstack cloud-framework-agent-lb @@ -632,6 +607,11 @@ cloud-plugin-backup-networker ${project.version} + + org.apache.cloudstack + cloud-plugin-backup-nas + ${project.version} + org.apache.cloudstack cloud-plugin-integrations-kubernetes-service @@ -639,7 +619,7 @@ org.apache.cloudstack - cloud-plugin-shutdown + cloud-plugin-maintenance ${project.version} @@ -652,11 +632,26 @@ cloud-plugin-storage-object-minio ${project.version} + + org.apache.cloudstack + cloud-plugin-storage-object-ceph + ${project.version} + + + org.apache.cloudstack + cloud-plugin-storage-object-cloudian + ${project.version} + org.apache.cloudstack cloud-plugin-storage-object-simulator ${project.version} + + org.apache.cloudstack + cloud-plugin-sharedfs-provider-storagevm + ${project.version} + org.apache.cloudstack cloud-usage @@ -671,16 +666,22 @@ - 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 + @@ -693,11 +694,11 @@ org.apache.cloudstack.ServerDaemon - ${git.branch} - ${git.tag} - ${git.revision} - ${git.revision} - ${git.branch} + ${scmBranch} + ${project.version} + ${buildNumber} + ${buildNumber} + ${scmBranch} @@ -709,8 +710,8 @@ - mysql - mysql-connector-java + com.mysql + mysql-connector-j ${cs.mysql.version} @@ -898,8 +899,8 @@ ${project.build.directory}/pythonlibs - mysql - mysql-connector-java + com.mysql + mysql-connector-j false ${project.build.directory}/lib @@ -973,7 +974,7 @@ org.bouncycastle:bcprov-jdk15on org.bouncycastle:bcpkix-jdk15on org.bouncycastle:bctls-jdk15on - mysql:mysql-connector-java + com.mysql:mysql-connector-j org.apache.cloudstack:cloud-plugin-storage-volume-storpool org.apache.cloudstack:cloud-plugin-storage-volume-linstor org.apache.cloudstack:cloud-usage @@ -1097,11 +1098,31 @@ cloud-plugin-network-cisco-vnmc ${project.version} + + org.apache.cloudstack + cloud-plugin-network-nsx + ${project.version} + + + org.apache.cloudstack + cloud-plugin-network-netris + ${project.version} + + + org.apache.cloudstack + cloud-plugin-network-tungsten + ${project.version} + org.apache.cloudstack cloud-plugin-api-vmware-sioc ${project.version} + + org.apache.cloudstack + cloud-plugin-network-contrail + ${project.version} + org.apache.cloudstack cloud-plugin-backup-veeam diff --git a/client/src/main/java/org/apache/cloudstack/ACSRequestLog.java b/client/src/main/java/org/apache/cloudstack/ACSRequestLog.java new file mode 100644 index 000000000000..249451120f1c --- /dev/null +++ b/client/src/main/java/org/apache/cloudstack/ACSRequestLog.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; + +import com.cloud.api.ApiServlet; +import com.cloud.utils.StringUtils; +import org.eclipse.jetty.server.NCSARequestLog; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.util.DateCache; +import org.eclipse.jetty.util.component.LifeCycle; + +import java.net.InetAddress; +import java.util.Locale; +import java.util.TimeZone; + +import static org.apache.commons.configuration.DataConfiguration.DEFAULT_DATE_FORMAT; + +public class ACSRequestLog extends NCSARequestLog { + private static final ThreadLocal buffers = + ThreadLocal.withInitial(() -> new StringBuilder(256)); + + private final DateCache dateCache; + + public ACSRequestLog() { + super(); + + TimeZone timeZone = TimeZone.getTimeZone("GMT"); + Locale locale = Locale.getDefault(); + dateCache = new DateCache(DEFAULT_DATE_FORMAT, locale, timeZone); + } + + @Override + public void log(Request request, Response response) { + String requestURI = StringUtils.cleanString(request.getOriginalURI()); + try { + StringBuilder sb = buffers.get(); + sb.setLength(0); + + InetAddress remoteAddress = ApiServlet.getClientAddress(request); + sb.append(remoteAddress.getHostAddress()) + .append(" - - [") + .append(dateCache.format(request.getTimeStamp())) + .append("] \"") + .append(request.getMethod()) + .append(" ") + .append(requestURI) + .append(" ") + .append(request.getProtocol()) + .append("\" ") + .append(response.getStatus()) + .append(" ") + .append(response.getHttpChannel().getBytesWritten()) // apply filter here? + .append(" \"-\" \"") + .append(request.getHeader("User-Agent")) + .append("\""); + + write(sb.toString()); + } catch (Exception e) { + LOG.warn("Unable to log request", e); + } + } + + @Override + protected void stop(LifeCycle lifeCycle) throws Exception { + buffers.remove(); + super.stop(lifeCycle); + } +} diff --git a/client/src/main/java/org/apache/cloudstack/ServerDaemon.java b/client/src/main/java/org/apache/cloudstack/ServerDaemon.java index 23312b8517f3..06477fff8986 100644 --- a/client/src/main/java/org/apache/cloudstack/ServerDaemon.java +++ b/client/src/main/java/org/apache/cloudstack/ServerDaemon.java @@ -30,10 +30,8 @@ import org.apache.commons.daemon.DaemonContext; import org.apache.commons.lang3.StringUtils; import org.eclipse.jetty.jmx.MBeanContainer; -import org.eclipse.jetty.server.ForwardedRequestCustomizer; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; -import org.eclipse.jetty.server.NCSARequestLog; import org.eclipse.jetty.server.RequestLog; import org.eclipse.jetty.server.SecureRequestCustomizer; import org.eclipse.jetty.server.Server; @@ -73,16 +71,15 @@ 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"; private static final String REQUEST_CONTENT_SIZE_KEY = "request.content.size"; private static final int DEFAULT_REQUEST_CONTENT_SIZE = 1048576; + private static final String REQUEST_MAX_FORM_KEYS_KEY = "request.max.form.keys"; + private static final int DEFAULT_REQUEST_MAX_FORM_KEYS = 5000; + private static final String THREADS_MIN = "threads.min"; + private static final String THREADS_MAX = "threads.max"; //////////////////////////////////////////////////////// /////////////// Server Configuration /////////////////// @@ -95,6 +92,7 @@ public class ServerDaemon implements Daemon { private int httpsPort = 8443; private int sessionTimeout = 30; private int maxFormContentSize = DEFAULT_REQUEST_CONTENT_SIZE; + private int maxFormKeys = DEFAULT_REQUEST_MAX_FORM_KEYS; private boolean httpsEnable = false; private String accessLogFile = "access.log"; private String bindInterface = null; @@ -102,6 +100,8 @@ public class ServerDaemon implements Daemon { private String keystoreFile; private String keystorePassword; private String webAppLocation; + private int minThreads; + private int maxThreads; ////////////////////////////////////////////////// /////////////// Public methods /////////////////// @@ -132,16 +132,19 @@ 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")); setSessionTimeout(Integer.valueOf(properties.getProperty(SESSION_TIMEOUT, "30"))); setMaxFormContentSize(Integer.valueOf(properties.getProperty(REQUEST_CONTENT_SIZE_KEY, String.valueOf(DEFAULT_REQUEST_CONTENT_SIZE)))); + setMaxFormKeys(Integer.valueOf(properties.getProperty(REQUEST_MAX_FORM_KEYS_KEY, String.valueOf(DEFAULT_REQUEST_MAX_FORM_KEYS)))); + setMinThreads(Integer.valueOf(properties.getProperty(THREADS_MIN, "10"))); + setMaxThreads(Integer.valueOf(properties.getProperty(THREADS_MAX, "500"))); } catch (final IOException e) { logger.warn("Failed to read configuration from server.properties file", e); } finally { @@ -159,8 +162,8 @@ public void init(final DaemonContext context) { public void start() throws Exception { // Thread pool final QueuedThreadPool threadPool = new QueuedThreadPool(); - threadPool.setMinThreads(10); - threadPool.setMaxThreads(500); + threadPool.setMinThreads(minThreads); + threadPool.setMaxThreads(maxThreads); // Jetty Server server = new Server(threadPool); @@ -174,7 +177,7 @@ public void start() throws Exception { // HTTP config final HttpConfiguration httpConfig = new HttpConfiguration(); - httpConfig.addCustomizer( new ForwardedRequestCustomizer() ); +// it would be nice to make this dynamic but we take care of this ourselves for now: httpConfig.addCustomizer( new ForwardedRequestCustomizer() ); httpConfig.setSecureScheme("https"); httpConfig.setSecurePort(httpsPort); httpConfig.setOutputBufferSize(32768); @@ -193,6 +196,7 @@ public void start() throws Exception { // Extra config options server.setStopAtShutdown(true); server.setAttribute(ContextHandler.MAX_FORM_CONTENT_SIZE_KEY, maxFormContentSize); + server.setAttribute(ContextHandler.MAX_FORM_KEYS_KEY, maxFormKeys); // HTTPS Connector createHttpsConnector(httpConfig); @@ -265,6 +269,7 @@ private Pair createHandlers() { webApp.setContextPath(contextPath); webApp.setInitParameter("org.eclipse.jetty.servlet.Default.dirAllowed", "false"); webApp.setMaxFormContentSize(maxFormContentSize); + webApp.setMaxFormKeys(maxFormKeys); // GZIP handler final GzipHandler gzipHandler = new GzipHandler(); @@ -294,7 +299,7 @@ private Pair createHandlers() { } private RequestLog createRequestLog() { - final NCSARequestLog log = new NCSARequestLog(); + final ACSRequestLog log = new ACSRequestLog(); final File logPath = new File(accessLogFile); final File parentFile = logPath.getParentFile(); if (parentFile != null) { @@ -367,4 +372,16 @@ public void setSessionTimeout(int sessionTimeout) { public void setMaxFormContentSize(int maxFormContentSize) { this.maxFormContentSize = maxFormContentSize; } + + public void setMaxFormKeys(int maxFormKeys) { + this.maxFormKeys = maxFormKeys; + } + + public void setMinThreads(int minThreads) { + this.minThreads = minThreads; + } + + public void setMaxThreads(int maxThreads) { + this.maxThreads = maxThreads; + } } diff --git a/client/src/main/resources/META-INF/cloudstack/webApplicationContext.xml b/client/src/main/resources/META-INF/cloudstack/webApplicationContext.xml index 3b3c6dbe35c2..e1c462686189 100644 --- a/client/src/main/resources/META-INF/cloudstack/webApplicationContext.xml +++ b/client/src/main/resources/META-INF/cloudstack/webApplicationContext.xml @@ -25,7 +25,7 @@ http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" - > + > 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 83cdee8cf4f1..f91a330b7ba9 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.0.0-SNAPSHOT + 4.23.0.0-SNAPSHOT diff --git a/core/src/main/java/com/cloud/agent/api/CheckConvertInstanceAnswer.java b/core/src/main/java/com/cloud/agent/api/CheckConvertInstanceAnswer.java new file mode 100644 index 000000000000..a02ac92927a9 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/CheckConvertInstanceAnswer.java @@ -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. +package com.cloud.agent.api; + +public class CheckConvertInstanceAnswer extends Answer { + + private boolean ovfExportSupported = false; + + public CheckConvertInstanceAnswer() { + super(); + } + + public CheckConvertInstanceAnswer(Command command, boolean success) { + super(command, success, ""); + } + + public CheckConvertInstanceAnswer(Command command, boolean success, String details) { + super(command, success, details); + } + + public CheckConvertInstanceAnswer(Command command, boolean success, boolean ovfExportSupported, String details) { + super(command, success, details); + this.ovfExportSupported = ovfExportSupported; + } + + public boolean isOvfExportSupported() { + return ovfExportSupported; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/CheckConvertInstanceCommand.java b/core/src/main/java/com/cloud/agent/api/CheckConvertInstanceCommand.java new file mode 100644 index 000000000000..fc066e5c5899 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/CheckConvertInstanceCommand.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 com.cloud.agent.api; + +public class CheckConvertInstanceCommand extends Command { + boolean checkWindowsGuestConversionSupport = false; + + public CheckConvertInstanceCommand() { + } + + public CheckConvertInstanceCommand(boolean checkWindowsGuestConversionSupport) { + this.checkWindowsGuestConversionSupport = checkWindowsGuestConversionSupport; + } + + @Override + public boolean executeInSequence() { + return false; + } + + public boolean getCheckWindowsGuestConversionSupport() { + return checkWindowsGuestConversionSupport; + } +} 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 982e10cf2ebe..94239f2900ef 100644 --- a/core/src/main/java/com/cloud/agent/api/CheckOnHostCommand.java +++ b/core/src/main/java/com/cloud/agent/api/CheckOnHostCommand.java @@ -35,8 +35,7 @@ public CheckOnHostCommand(Host host) { } public CheckOnHostCommand(Host host, boolean reportCheckFailureIfOneStorageIsDown) { - super(); - this.host = new HostTO(host); + this(host); this.reportCheckFailureIfOneStorageIsDown = reportCheckFailureIfOneStorageIsDown; } diff --git a/core/src/main/java/com/cloud/agent/api/CheckS2SVpnConnectionsAnswer.java b/core/src/main/java/com/cloud/agent/api/CheckS2SVpnConnectionsAnswer.java index b299c602dde5..351702a048ca 100644 --- a/core/src/main/java/com/cloud/agent/api/CheckS2SVpnConnectionsAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/CheckS2SVpnConnectionsAnswer.java @@ -25,7 +25,6 @@ public class CheckS2SVpnConnectionsAnswer extends Answer { Map ipToConnected; Map ipToDetail; - String details; protected CheckS2SVpnConnectionsAnswer() { ipToConnected = new HashMap(); diff --git a/core/src/main/java/com/cloud/agent/api/CheckVolumeAnswer.java b/core/src/main/java/com/cloud/agent/api/CheckVolumeAnswer.java index dd136d8642f6..07b7e102df96 100644 --- a/core/src/main/java/com/cloud/agent/api/CheckVolumeAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/CheckVolumeAnswer.java @@ -17,23 +17,33 @@ package com.cloud.agent.api; -@LogLevel(LogLevel.Log4jLevel.Trace) +import org.apache.cloudstack.storage.volume.VolumeOnStorageTO; + +import java.util.Map; + public class CheckVolumeAnswer extends Answer { private long size; + private Map volumeDetails; CheckVolumeAnswer() { } - public CheckVolumeAnswer(CheckVolumeCommand cmd, String details, long size) { - super(cmd, true, details); + public CheckVolumeAnswer(CheckVolumeCommand cmd, final boolean success, String details, long size, + Map volumeDetails) { + super(cmd, success, details); this.size = size; + this.volumeDetails = volumeDetails; } public long getSize() { return size; } + public Map getVolumeDetails() { + return volumeDetails; + } + public String getString() { return "CheckVolumeAnswer [size=" + size + "]"; } diff --git a/core/src/main/java/com/cloud/agent/api/CheckVolumeCommand.java b/core/src/main/java/com/cloud/agent/api/CheckVolumeCommand.java index b4036bebf3ac..bd44b35c8954 100644 --- a/core/src/main/java/com/cloud/agent/api/CheckVolumeCommand.java +++ b/core/src/main/java/com/cloud/agent/api/CheckVolumeCommand.java @@ -21,7 +21,6 @@ import com.cloud.agent.api.to.StorageFilerTO; -@LogLevel(LogLevel.Log4jLevel.Trace) public class CheckVolumeCommand extends Command { String srcFile; diff --git a/core/src/main/java/com/cloud/agent/api/CleanupVMCommand.java b/core/src/main/java/com/cloud/agent/api/CleanupVMCommand.java new file mode 100644 index 000000000000..a4d73a8b1648 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/CleanupVMCommand.java @@ -0,0 +1,46 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package com.cloud.agent.api; + +/** + * This command will destroy a leftover VM during the expunge process if it wasn't destroyed before. + * + */ +public class CleanupVMCommand extends Command { + String vmName; + boolean executeInSequence; + + public CleanupVMCommand(String vmName) { + this(vmName, false); + } + public CleanupVMCommand(String vmName, boolean executeInSequence) { + this.vmName = vmName; + this.executeInSequence = executeInSequence; + } + + @Override + public boolean executeInSequence() { + return executeInSequence; + } + + public String getVmName() { + return vmName; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/ConsoleAccessAuthenticationCommand.java b/core/src/main/java/com/cloud/agent/api/ConsoleAccessAuthenticationCommand.java index 683d4afd5b2f..ac6f15ec4c32 100644 --- a/core/src/main/java/com/cloud/agent/api/ConsoleAccessAuthenticationCommand.java +++ b/core/src/main/java/com/cloud/agent/api/ConsoleAccessAuthenticationCommand.java @@ -27,6 +27,7 @@ public class ConsoleAccessAuthenticationCommand extends AgentControlCommand { private String _sid; private String _ticket; private String sessionUuid; + private String clientAddress; private boolean _isReauthenticating; @@ -35,13 +36,14 @@ public ConsoleAccessAuthenticationCommand() { } public ConsoleAccessAuthenticationCommand(String host, String port, String vmId, String sid, String ticket, - String sessiontkn) { + String sessiontkn, String clientAddress) { _host = host; _port = port; _vmId = vmId; _sid = sid; _ticket = ticket; sessionUuid = sessiontkn; + this.clientAddress = clientAddress; } public String getHost() { @@ -79,4 +81,12 @@ public String getSessionUuid() { public void setSessionUuid(String sessionUuid) { this.sessionUuid = sessionUuid; } + + public String getClientAddress() { + return clientAddress; + } + + public void setClientAddress(String clientAddress) { + this.clientAddress = clientAddress; + } } diff --git a/core/src/main/java/com/cloud/agent/api/ConvertInstanceAnswer.java b/core/src/main/java/com/cloud/agent/api/ConvertInstanceAnswer.java index 829888570a60..8092ab9b43f5 100644 --- a/core/src/main/java/com/cloud/agent/api/ConvertInstanceAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/ConvertInstanceAnswer.java @@ -16,25 +16,20 @@ // under the License. package com.cloud.agent.api; -import org.apache.cloudstack.vm.UnmanagedInstanceTO; - public class ConvertInstanceAnswer extends Answer { + private String temporaryConvertUuid; + public ConvertInstanceAnswer() { super(); } - private UnmanagedInstanceTO convertedInstance; - - public ConvertInstanceAnswer(Command command, boolean success, String details) { - super(command, success, details); - } - public ConvertInstanceAnswer(Command command, UnmanagedInstanceTO convertedInstance) { + public ConvertInstanceAnswer(Command command, String temporaryConvertUuid) { super(command, true, ""); - this.convertedInstance = convertedInstance; + this.temporaryConvertUuid = temporaryConvertUuid; } - public UnmanagedInstanceTO getConvertedInstance() { - return convertedInstance; + public String getTemporaryConvertUuid() { + return temporaryConvertUuid; } } 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 63234b044807..24336747ccf4 100644 --- a/core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java +++ b/core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java @@ -20,42 +20,76 @@ import com.cloud.agent.api.to.RemoteInstanceTO; import com.cloud.hypervisor.Hypervisor; -import java.util.List; - public class ConvertInstanceCommand extends Command { private RemoteInstanceTO sourceInstance; + private String originalVMName; private Hypervisor.HypervisorType destinationHypervisorType; - private List destinationStoragePools; private DataStoreTO conversionTemporaryLocation; + private String templateDirOnConversionLocation; + private boolean checkConversionSupport; + private boolean exportOvfToConversionLocation; + private int threadsCountToExportOvf = 0; + private String extraParams; public ConvertInstanceCommand() { } - public ConvertInstanceCommand(RemoteInstanceTO sourceInstance, Hypervisor.HypervisorType destinationHypervisorType, - List destinationStoragePools, DataStoreTO conversionTemporaryLocation) { + public ConvertInstanceCommand(RemoteInstanceTO sourceInstance, Hypervisor.HypervisorType destinationHypervisorType, DataStoreTO conversionTemporaryLocation, + String templateDirOnConversionLocation, boolean checkConversionSupport, boolean exportOvfToConversionLocation, String sourceVMName) { this.sourceInstance = sourceInstance; this.destinationHypervisorType = destinationHypervisorType; - this.destinationStoragePools = destinationStoragePools; this.conversionTemporaryLocation = conversionTemporaryLocation; + this.templateDirOnConversionLocation = templateDirOnConversionLocation; + this.checkConversionSupport = checkConversionSupport; + this.exportOvfToConversionLocation = exportOvfToConversionLocation; + this.originalVMName = sourceVMName; } public RemoteInstanceTO getSourceInstance() { return sourceInstance; } - public Hypervisor.HypervisorType getDestinationHypervisorType() { - return destinationHypervisorType; + public String getOriginalVMName() { + return originalVMName; } - public List getDestinationStoragePools() { - return destinationStoragePools; + public Hypervisor.HypervisorType getDestinationHypervisorType() { + return destinationHypervisorType; } public DataStoreTO getConversionTemporaryLocation() { return conversionTemporaryLocation; } + public String getTemplateDirOnConversionLocation() { + return templateDirOnConversionLocation; + } + + public boolean getCheckConversionSupport() { + return checkConversionSupport; + } + + public boolean getExportOvfToConversionLocation() { + return exportOvfToConversionLocation; + } + + public int getThreadsCountToExportOvf() { + return threadsCountToExportOvf; + } + + public void setThreadsCountToExportOvf(int threadsCountToExportOvf) { + this.threadsCountToExportOvf = threadsCountToExportOvf; + } + + public String getExtraParams() { + return extraParams; + } + + public void setExtraParams(String extraParams) { + this.extraParams = extraParams; + } + @Override public boolean executeInSequence() { return false; diff --git a/core/src/main/java/com/cloud/agent/api/ConvertSnapshotAnswer.java b/core/src/main/java/com/cloud/agent/api/ConvertSnapshotAnswer.java new file mode 100644 index 000000000000..c19a061f736c --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/ConvertSnapshotAnswer.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.cloud.agent.api; + +import org.apache.cloudstack.storage.to.SnapshotObjectTO; + +public class ConvertSnapshotAnswer extends Answer { + + private SnapshotObjectTO snapshotObjectTO; + + public ConvertSnapshotAnswer(SnapshotObjectTO snapshotObjectTO) { + super(null); + this.snapshotObjectTO = snapshotObjectTO; + } + + public SnapshotObjectTO getSnapshotObjectTO() { + return snapshotObjectTO; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/ConvertSnapshotCommand.java b/core/src/main/java/com/cloud/agent/api/ConvertSnapshotCommand.java new file mode 100644 index 000000000000..e456d4b6b122 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/ConvertSnapshotCommand.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.cloud.agent.api; + +import org.apache.cloudstack.storage.to.SnapshotObjectTO; + +public class ConvertSnapshotCommand extends Command { + + public static final String TEMP_SNAPSHOT_NAME = "_temp"; + + SnapshotObjectTO snapshotObjectTO; + + public SnapshotObjectTO getSnapshotObjectTO() { + return snapshotObjectTO; + } + + public ConvertSnapshotCommand(SnapshotObjectTO snapshotObjectTO) { + this.snapshotObjectTO = snapshotObjectTO; + } + + @Override + public boolean executeInSequence() { + return true; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeAnswer.java b/core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeAnswer.java index f6d7cab45964..4aec0b26581b 100644 --- a/core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeAnswer.java @@ -17,22 +17,28 @@ package com.cloud.agent.api; -@LogLevel(LogLevel.Log4jLevel.Trace) +import org.apache.cloudstack.storage.volume.VolumeOnStorageTO; + +import java.util.Map; + public class CopyRemoteVolumeAnswer extends Answer { private String remoteIp; private String filename; private long size; + private Map volumeDetails; CopyRemoteVolumeAnswer() { } - public CopyRemoteVolumeAnswer(CopyRemoteVolumeCommand cmd, String details, String filename, long size) { - super(cmd, true, details); + public CopyRemoteVolumeAnswer(CopyRemoteVolumeCommand cmd, final boolean success, String details, String filename, long size, + Map volumeDetails) { + super(cmd, success, details); this.remoteIp = cmd.getRemoteIp(); this.filename = filename; this.size = size; + this.volumeDetails = volumeDetails; } public String getRemoteIp() { @@ -55,6 +61,10 @@ public long getSize() { return size; } + public Map getVolumeDetails() { + return volumeDetails; + } + public String getString() { return "CopyRemoteVolumeAnswer [remoteIp=" + remoteIp + "]"; } diff --git a/core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeCommand.java b/core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeCommand.java index 82bc4d7cb459..798336b0e72c 100644 --- a/core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeCommand.java +++ b/core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeCommand.java @@ -21,16 +21,13 @@ import com.cloud.agent.api.to.StorageFilerTO; -@LogLevel(LogLevel.Log4jLevel.Trace) public class CopyRemoteVolumeCommand extends Command { - String remoteIp; String username; + @LogLevel(LogLevel.Log4jLevel.Off) String password; String srcFile; - String tmpPath; - StorageFilerTO storageFilerTO; public CopyRemoteVolumeCommand(String remoteIp, String username, String password) { diff --git a/core/src/main/java/com/cloud/agent/api/DeleteStoragePoolCommand.java b/core/src/main/java/com/cloud/agent/api/DeleteStoragePoolCommand.java index 969dd2eb801c..84ca39f15464 100644 --- a/core/src/main/java/com/cloud/agent/api/DeleteStoragePoolCommand.java +++ b/core/src/main/java/com/cloud/agent/api/DeleteStoragePoolCommand.java @@ -21,10 +21,10 @@ import java.io.File; import java.util.Map; -import java.util.UUID; import com.cloud.agent.api.to.StorageFilerTO; import com.cloud.storage.StoragePool; +import com.cloud.utils.UuidUtils; public class DeleteStoragePoolCommand extends Command { public static final String DATASTORE_NAME = "datastoreName"; @@ -49,7 +49,7 @@ public DeleteStoragePoolCommand(StoragePool pool, String localPath) { } public DeleteStoragePoolCommand(StoragePool pool) { - this(pool, LOCAL_PATH_PREFIX + File.separator + UUID.nameUUIDFromBytes((pool.getHostAddress() + pool.getPath()).getBytes())); + this(pool, LOCAL_PATH_PREFIX + File.separator + UuidUtils.nameUUIDFromBytes((pool.getHostAddress() + pool.getPath()).getBytes())); } public void setPool(StoragePool pool) { diff --git a/core/src/main/java/com/cloud/agent/api/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/GetGPUStatsAnswer.java b/core/src/main/java/com/cloud/agent/api/GetGPUStatsAnswer.java index 8b3cd44e207f..5bf70ed086f6 100644 --- a/core/src/main/java/com/cloud/agent/api/GetGPUStatsAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/GetGPUStatsAnswer.java @@ -19,7 +19,9 @@ package com.cloud.agent.api; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import com.cloud.agent.api.LogLevel.Log4jLevel; @@ -27,6 +29,7 @@ public class GetGPUStatsAnswer extends Answer { private HashMap> groupDetails; + private List gpuDevices = new ArrayList<>(); public GetGPUStatsAnswer(final GetGPUStatsCommand cmd, final HashMap> groupDetails) { super(cmd); @@ -37,7 +40,21 @@ public GetGPUStatsAnswer(final GetGPUStatsCommand cmd, final boolean success, fi super(cmd, success, details); } + public GetGPUStatsAnswer(final GetGPUStatsCommand cmd, final List gpuDevices) { + super(cmd); + this.gpuDevices = gpuDevices; + } + + public HashMap> getGroupDetails() { return groupDetails; } + + public List getGpuDevices() { + return gpuDevices; + } + + public void setGpuDevices(List gpuDevices) { + this.gpuDevices = gpuDevices; + } } diff --git a/core/src/main/java/com/cloud/agent/api/GetRemoteVmsAnswer.java b/core/src/main/java/com/cloud/agent/api/GetRemoteVmsAnswer.java index 8cd072f1da1d..c4e590591d0c 100644 --- a/core/src/main/java/com/cloud/agent/api/GetRemoteVmsAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/GetRemoteVmsAnswer.java @@ -22,10 +22,10 @@ import java.util.HashMap; import java.util.List; -@LogLevel(LogLevel.Log4jLevel.Trace) public class GetRemoteVmsAnswer extends Answer { private String remoteIp; + @LogLevel(LogLevel.Log4jLevel.Trace) private HashMap unmanagedInstances; List vmNames; diff --git a/core/src/main/java/com/cloud/agent/api/GetRemoteVmsCommand.java b/core/src/main/java/com/cloud/agent/api/GetRemoteVmsCommand.java index 5c71d12dbd08..5b6b9bdd3606 100644 --- a/core/src/main/java/com/cloud/agent/api/GetRemoteVmsCommand.java +++ b/core/src/main/java/com/cloud/agent/api/GetRemoteVmsCommand.java @@ -19,11 +19,11 @@ package com.cloud.agent.api; -@LogLevel(LogLevel.Log4jLevel.Trace) public class GetRemoteVmsCommand extends Command { String remoteIp; String username; + @LogLevel(LogLevel.Log4jLevel.Off) String password; public GetRemoteVmsCommand(String remoteIp, String username, String password) { diff --git a/core/src/main/java/com/cloud/agent/api/GetStorageStatsAnswer.java b/core/src/main/java/com/cloud/agent/api/GetStorageStatsAnswer.java index 26e7b7495869..79753661066e 100644 --- a/core/src/main/java/com/cloud/agent/api/GetStorageStatsAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/GetStorageStatsAnswer.java @@ -27,24 +27,46 @@ public class GetStorageStatsAnswer extends Answer implements StorageStats { protected GetStorageStatsAnswer() { } - protected long used; + protected long usedBytes; - protected long capacity; + protected long capacityBytes; + + protected Long capacityIops; + + protected Long usedIops; @Override public long getByteUsed() { - return used; + return usedBytes; } @Override public long getCapacityBytes() { - return capacity; + return capacityBytes; + } + + @Override + public Long getCapacityIops() { + return capacityIops; + } + + @Override + public Long getUsedIops() { + return usedIops; + } + + public GetStorageStatsAnswer(GetStorageStatsCommand cmd, long capacityBytes, long usedBytes) { + super(cmd, true, null); + this.capacityBytes = capacityBytes; + this.usedBytes = usedBytes; } - public GetStorageStatsAnswer(GetStorageStatsCommand cmd, long capacity, long used) { + public GetStorageStatsAnswer(GetStorageStatsCommand cmd, long capacityBytes, long usedBytes, Long capacityIops, Long usedIops) { super(cmd, true, null); - this.capacity = capacity; - this.used = used; + this.capacityBytes = capacityBytes; + this.usedBytes = usedBytes; + this.capacityIops = capacityIops; + this.usedIops = usedIops; } public GetStorageStatsAnswer(GetStorageStatsCommand cmd, String details) { diff --git a/core/src/main/java/com/cloud/agent/api/GetUnmanagedInstancesAnswer.java b/core/src/main/java/com/cloud/agent/api/GetUnmanagedInstancesAnswer.java index 771d472be2ae..950930ec6149 100644 --- a/core/src/main/java/com/cloud/agent/api/GetUnmanagedInstancesAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/GetUnmanagedInstancesAnswer.java @@ -21,10 +21,10 @@ import org.apache.cloudstack.vm.UnmanagedInstanceTO; -@LogLevel(LogLevel.Log4jLevel.Trace) public class GetUnmanagedInstancesAnswer extends Answer { private String instanceName; + @LogLevel(LogLevel.Log4jLevel.Trace) private HashMap unmanagedInstances; GetUnmanagedInstancesAnswer() { diff --git a/core/src/main/java/com/cloud/agent/api/GetUnmanagedInstancesCommand.java b/core/src/main/java/com/cloud/agent/api/GetUnmanagedInstancesCommand.java index 2cd80aebea1b..c0b8987e152d 100644 --- a/core/src/main/java/com/cloud/agent/api/GetUnmanagedInstancesCommand.java +++ b/core/src/main/java/com/cloud/agent/api/GetUnmanagedInstancesCommand.java @@ -28,10 +28,10 @@ * All managed instances will be filtered while trying to find unmanaged instances. */ -@LogLevel(LogLevel.Log4jLevel.Trace) public class GetUnmanagedInstancesCommand extends Command { String instanceName; + @LogLevel(LogLevel.Log4jLevel.Trace) List managedInstancesNames; public GetUnmanagedInstancesCommand() { diff --git a/core/src/main/java/com/cloud/agent/api/GetVmIpAddressCommand.java b/core/src/main/java/com/cloud/agent/api/GetVmIpAddressCommand.java index a9c7413b9f07..4efc0781f919 100644 --- a/core/src/main/java/com/cloud/agent/api/GetVmIpAddressCommand.java +++ b/core/src/main/java/com/cloud/agent/api/GetVmIpAddressCommand.java @@ -24,11 +24,13 @@ public class GetVmIpAddressCommand extends Command { String vmName; String vmNetworkCidr; boolean windows = false; + String macAddress; - public GetVmIpAddressCommand(String vmName, String vmNetworkCidr, boolean windows) { + public GetVmIpAddressCommand(String vmName, String vmNetworkCidr, boolean windows, String macAddress) { this.vmName = vmName; this.windows = windows; this.vmNetworkCidr = vmNetworkCidr; + this.macAddress = macAddress; } @Override @@ -47,4 +49,8 @@ public boolean isWindows(){ public String getVmNetworkCidr() { return vmNetworkCidr; } + + public String getMacAddress() { + return macAddress; + } } diff --git a/core/src/main/java/com/cloud/agent/api/GetVolumeStatAnswer.java b/core/src/main/java/com/cloud/agent/api/GetVolumeStatAnswer.java new file mode 100644 index 000000000000..8352c97c108b --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/GetVolumeStatAnswer.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 com.cloud.agent.api; + +import com.cloud.agent.api.LogLevel.Log4jLevel; +import com.cloud.storage.Storage.StoragePoolType; + +@LogLevel(Log4jLevel.Trace) +public class GetVolumeStatAnswer extends Answer { + String poolUuid; + StoragePoolType poolType; + String volumePath; + long size = 0; + long virtualSize = 0; + + public GetVolumeStatAnswer(GetVolumeStatCommand cmd, long size, long virtualSize) { + super(cmd, true, ""); + this.poolUuid = cmd.getPoolUuid(); + this.poolType = cmd.getPoolType(); + this.volumePath = cmd.getVolumePath(); + this.size = size; + this.virtualSize = virtualSize; + } + + public GetVolumeStatAnswer(GetVolumeStatCommand cmd, boolean success, String details) { + super(cmd, success, details); + } + + protected GetVolumeStatAnswer() { + //no-args constructor for json serialization-deserialization + } + + public String getPoolUuid() { + return poolUuid; + } + + public void setPoolUuid(String poolUuid) { + this.poolUuid = poolUuid; + } + + public StoragePoolType getPoolType() { + return poolType; + } + + public void setPoolType(StoragePoolType poolType) { + this.poolType = poolType; + } + + public long getSize() { + return size; + } + + public void setSize(long size) { + this.size = size; + } + + public long getVirtualSize() { + return virtualSize; + } + + public void setVirtualSize(long virtualSize) { + this.virtualSize = virtualSize; + } + + public String getString() { + return "GetVolumeStatAnswer [poolUuid=" + poolUuid + ", poolType=" + poolType + ", volumePath=" + volumePath + ", size=" + size + ", virtualSize=" + virtualSize + "]"; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/GetVolumeStatCommand.java b/core/src/main/java/com/cloud/agent/api/GetVolumeStatCommand.java new file mode 100644 index 000000000000..1be3d6a04195 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/GetVolumeStatCommand.java @@ -0,0 +1,72 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package com.cloud.agent.api; + +import com.cloud.agent.api.LogLevel.Log4jLevel; +import com.cloud.storage.Storage.StoragePoolType; + +@LogLevel(Log4jLevel.Trace) +public class GetVolumeStatCommand extends Command { + String volumePath; + StoragePoolType poolType; + String poolUuid; + + protected GetVolumeStatCommand() { + } + + public GetVolumeStatCommand(String volumePath, StoragePoolType poolType, String poolUuid) { + this.volumePath = volumePath; + this.poolType = poolType; + this.poolUuid = poolUuid; + } + + public String getVolumePath() { + return volumePath; + } + + public void setVolumePath(String volumePath) { + this.volumePath = volumePath; + } + + public StoragePoolType getPoolType() { + return poolType; + } + + public void setPoolType(StoragePoolType poolType) { + this.poolType = poolType; + } + + public String getPoolUuid() { + return poolUuid; + } + + public void setPoolUuid(String storeUuid) { + this.poolUuid = storeUuid; + } + + @Override + public boolean executeInSequence() { + return false; + } + + public String getString() { + return "GetVolumeStatCommand [volumePath=" + volumePath + ", poolType=" + poolType + ", poolUuid=" + poolUuid + "]"; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/GetVolumesOnStorageAnswer.java b/core/src/main/java/com/cloud/agent/api/GetVolumesOnStorageAnswer.java new file mode 100644 index 000000000000..3c46994499d9 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/GetVolumesOnStorageAnswer.java @@ -0,0 +1,42 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package com.cloud.agent.api; + +import org.apache.cloudstack.storage.volume.VolumeOnStorageTO; + +import java.util.List; + +public class GetVolumesOnStorageAnswer extends Answer { + private List volumes; + + GetVolumesOnStorageAnswer() { + } + + public GetVolumesOnStorageAnswer(GetVolumesOnStorageCommand cmd, List volumes) { + super(cmd, true, null); + this.volumes = volumes; + } + + public GetVolumesOnStorageAnswer(final GetVolumesOnStorageCommand cmd, final boolean success, final String details) { + super(cmd, success, details); + } + + public List getVolumes() { + return volumes; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/GetVolumesOnStorageCommand.java b/core/src/main/java/com/cloud/agent/api/GetVolumesOnStorageCommand.java new file mode 100644 index 000000000000..6bc3356eb671 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/GetVolumesOnStorageCommand.java @@ -0,0 +1,55 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package com.cloud.agent.api; + +import com.cloud.agent.api.to.StorageFilerTO; + +public class GetVolumesOnStorageCommand extends Command { + + StorageFilerTO pool; + private String volumePath; //search by file path + private String keyword; //filter by keyword + + public GetVolumesOnStorageCommand() { + } + + public GetVolumesOnStorageCommand(StorageFilerTO pool, String filePath, String keyword) { + this.pool = pool; + this.volumePath = filePath; + this.keyword = keyword; + } + + public StorageFilerTO getPool() { + return pool; + } + + public String getVolumePath() { + return volumePath; + } + + public String getKeyword() { + return keyword; + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/HandleCksIsoCommand.java b/core/src/main/java/com/cloud/agent/api/HandleCksIsoCommand.java new file mode 100644 index 000000000000..16942bb05d44 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/HandleCksIsoCommand.java @@ -0,0 +1,34 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +package com.cloud.agent.api; + +import com.cloud.agent.api.routing.NetworkElementCommand; + +public class HandleCksIsoCommand extends NetworkElementCommand { + + private boolean mountCksIso; + + public HandleCksIsoCommand(boolean mountCksIso) { + this.mountCksIso = mountCksIso; + } + + public boolean isMountCksIso() { + return mountCksIso; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/ImportConvertedInstanceAnswer.java b/core/src/main/java/com/cloud/agent/api/ImportConvertedInstanceAnswer.java new file mode 100644 index 000000000000..2a8f8704e3f5 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/ImportConvertedInstanceAnswer.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 com.cloud.agent.api; + +import org.apache.cloudstack.vm.UnmanagedInstanceTO; + +public class ImportConvertedInstanceAnswer extends Answer { + + public ImportConvertedInstanceAnswer() { + super(); + } + private UnmanagedInstanceTO convertedInstance; + + public ImportConvertedInstanceAnswer(Command command, boolean success, String details) { + super(command, success, details); + } + + public ImportConvertedInstanceAnswer(Command command, UnmanagedInstanceTO convertedInstance) { + super(command, true, ""); + this.convertedInstance = convertedInstance; + } + + public UnmanagedInstanceTO getConvertedInstance() { + return convertedInstance; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/ImportConvertedInstanceCommand.java b/core/src/main/java/com/cloud/agent/api/ImportConvertedInstanceCommand.java new file mode 100644 index 000000000000..eadfa6556f86 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/ImportConvertedInstanceCommand.java @@ -0,0 +1,70 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.agent.api; + +import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.RemoteInstanceTO; + +import java.util.List; + +public class ImportConvertedInstanceCommand extends Command { + + private RemoteInstanceTO sourceInstance; + private List destinationStoragePools; + private DataStoreTO conversionTemporaryLocation; + private String temporaryConvertUuid; + private boolean forceConvertToPool; + + public ImportConvertedInstanceCommand() { + } + + public ImportConvertedInstanceCommand(RemoteInstanceTO sourceInstance, + List destinationStoragePools, + DataStoreTO conversionTemporaryLocation, String temporaryConvertUuid, + boolean forceConvertToPool) { + this.sourceInstance = sourceInstance; + this.destinationStoragePools = destinationStoragePools; + this.conversionTemporaryLocation = conversionTemporaryLocation; + this.temporaryConvertUuid = temporaryConvertUuid; + this.forceConvertToPool = forceConvertToPool; + } + + public RemoteInstanceTO getSourceInstance() { + return sourceInstance; + } + + public List getDestinationStoragePools() { + return destinationStoragePools; + } + + public DataStoreTO getConversionTemporaryLocation() { + return conversionTemporaryLocation; + } + + 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/MigrateAgentConnectionAnswer.java b/core/src/main/java/com/cloud/agent/api/MigrateAgentConnectionAnswer.java new file mode 100644 index 000000000000..33d32c7f6ccb --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/MigrateAgentConnectionAnswer.java @@ -0,0 +1,38 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package com.cloud.agent.api; + +public class MigrateAgentConnectionAnswer extends Answer { + public MigrateAgentConnectionAnswer() { + } + + public MigrateAgentConnectionAnswer(boolean result) { + this.result = result; + } + + public MigrateAgentConnectionAnswer(String details) { + this.result = false; + this.details = details; + } + + public MigrateAgentConnectionAnswer(MigrateAgentConnectionCommand cmd, boolean result) { + super(cmd, result, null); + } +} diff --git a/core/src/main/java/com/cloud/agent/api/MigrateAgentConnectionCommand.java b/core/src/main/java/com/cloud/agent/api/MigrateAgentConnectionCommand.java new file mode 100644 index 000000000000..9471a68669fe --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/MigrateAgentConnectionCommand.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 java.util.List; + +public class MigrateAgentConnectionCommand extends Command { + private List msList; + private List avoidMsList; + private String lbAlgorithm; + private Long lbCheckInterval; + + public MigrateAgentConnectionCommand() { + } + + public MigrateAgentConnectionCommand(final List msList, final List avoidMsList, final String lbAlgorithm, final Long lbCheckInterval) { + super(); + this.msList = msList; + this.avoidMsList = avoidMsList; + this.lbAlgorithm = lbAlgorithm; + this.lbCheckInterval = lbCheckInterval; + } + + public List getMsList() { + return msList; + } + + public List getAvoidMsList() { + return avoidMsList; + } + + public String getLbAlgorithm() { + return lbAlgorithm; + } + + public Long getLbCheckInterval() { + return lbCheckInterval; + } + + @Override + public boolean executeInSequence() { + return false; + } +} 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 3acdb9c351b5..5ac4e9ae445e 100644 --- a/core/src/main/java/com/cloud/agent/api/MigrateCommand.java +++ b/core/src/main/java/com/cloud/agent/api/MigrateCommand.java @@ -29,14 +29,14 @@ public class MigrateCommand extends Command { private String vmName; - private String destIp; + private String destinationIp; private Map migrateStorage; private boolean migrateStorageManaged; private boolean migrateNonSharedInc; private boolean autoConvergence; private String hostGuid; - private boolean isWindows; - private VirtualMachineTO vmTO; + private boolean windows; + private VirtualMachineTO virtualMachine; private boolean executeInSequence = false; private List migrateDiskInfoList = new ArrayList<>(); private Map dpdkInterfaceMapping = new HashMap<>(); @@ -64,11 +64,11 @@ public void setVlanToPersistenceMap(Map vlanToPersistenceMap) { protected MigrateCommand() { } - public MigrateCommand(String vmName, String destIp, boolean isWindows, VirtualMachineTO vmTO, boolean executeInSequence) { + public MigrateCommand(String vmName, String destinationIp, boolean windows, VirtualMachineTO virtualMachine, boolean executeInSequence) { this.vmName = vmName; - this.destIp = destIp; - this.isWindows = isWindows; - this.vmTO = vmTO; + this.destinationIp = destinationIp; + this.windows = windows; + this.virtualMachine = virtualMachine; this.executeInSequence = executeInSequence; } @@ -105,15 +105,15 @@ public boolean isAutoConvergence() { } public boolean isWindows() { - return isWindows; + return windows; } public VirtualMachineTO getVirtualMachine() { - return vmTO; + return virtualMachine; } public String getDestinationIp() { - return destIp; + return destinationIp; } public String getVmName() { @@ -233,4 +233,9 @@ public void setSourceDiskOnStorageFileSystem(boolean isDiskOnFileSystemStorage) this.isSourceDiskOnStorageFileSystem = isDiskOnFileSystemStorage; } } + + @Override + public boolean isReconcile() { + return true; + } } diff --git a/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolAnswer.java b/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolAnswer.java index 552ffb85aaf6..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,14 @@ 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); + } + public void setPoolInfo(StoragePoolInfo poolInfo) { this.poolInfo = poolInfo; } diff --git a/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolCommand.java b/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolCommand.java index ad05fe1d615d..a72000d73995 100644 --- a/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolCommand.java +++ b/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolCommand.java @@ -21,10 +21,10 @@ import java.io.File; import java.util.Map; -import java.util.UUID; import com.cloud.agent.api.to.StorageFilerTO; import com.cloud.storage.StoragePool; +import com.cloud.utils.UuidUtils; public class ModifyStoragePoolCommand extends Command { public static final String LOCAL_PATH_PREFIX = "/mnt/"; @@ -46,8 +46,12 @@ public ModifyStoragePoolCommand(boolean add, StoragePool pool, String localPath, this.details = details; } + public ModifyStoragePoolCommand(boolean add, StoragePool pool, Map details) { + this(add, pool, LOCAL_PATH_PREFIX + File.separator + UuidUtils.nameUUIDFromBytes((pool.getHostAddress() + pool.getPath()).getBytes()), details); + } + public ModifyStoragePoolCommand(boolean add, StoragePool pool) { - this(add, pool, LOCAL_PATH_PREFIX + File.separator + UUID.nameUUIDFromBytes((pool.getHostAddress() + pool.getPath()).getBytes())); + this(add, pool, LOCAL_PATH_PREFIX + File.separator + UuidUtils.nameUUIDFromBytes((pool.getHostAddress() + pool.getPath()).getBytes())); } public boolean getAdd() { diff --git a/core/src/main/java/com/cloud/agent/api/PingAnswer.java b/core/src/main/java/com/cloud/agent/api/PingAnswer.java index 6353b121583a..22eb4443f8a4 100644 --- a/core/src/main/java/com/cloud/agent/api/PingAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/PingAnswer.java @@ -19,18 +19,25 @@ package com.cloud.agent.api; +import java.util.ArrayList; +import java.util.List; + public class PingAnswer extends Answer { private PingCommand _command = null; private boolean sendStartup = false; + private List avoidMsList; + + private List reconcileCommands = new ArrayList<>(); protected PingAnswer() { } - public PingAnswer(PingCommand cmd, boolean sendStartup) { + public PingAnswer(PingCommand cmd, List avoidMsList, boolean sendStartup) { super(cmd); _command = cmd; this.sendStartup = sendStartup; + this.avoidMsList = avoidMsList; } public PingCommand getCommand() { @@ -44,4 +51,20 @@ public boolean isSendStartup() { public void setSendStartup(boolean sendStartup) { this.sendStartup = sendStartup; } + + public List getReconcileCommands() { + return reconcileCommands; + } + + public void setReconcileCommands(List reconcileCommands) { + this.reconcileCommands = reconcileCommands; + } + + public void addReconcileCommand(String reconcileCommand) { + this.reconcileCommands.add(reconcileCommand); + } + + public List getAvoidMsList() { + return avoidMsList; + } } diff --git a/core/src/main/java/com/cloud/agent/api/PingCommand.java b/core/src/main/java/com/cloud/agent/api/PingCommand.java index 4192fc2e7474..4ae64da89c3c 100644 --- a/core/src/main/java/com/cloud/agent/api/PingCommand.java +++ b/core/src/main/java/com/cloud/agent/api/PingCommand.java @@ -20,11 +20,14 @@ package com.cloud.agent.api; import com.cloud.host.Host; +import org.apache.cloudstack.command.CommandInfo; public class PingCommand extends Command { Host.Type hostType; long hostId; boolean outOfBand; + @LogLevel(LogLevel.Log4jLevel.Trace) + private CommandInfo[] commandInfos = new CommandInfo[] {}; protected PingCommand() { } @@ -78,4 +81,12 @@ public int hashCode() { result = 31 * result + (int) (hostId ^ (hostId >>> 32)); return result; } + + public CommandInfo[] getCommandInfos() { + return commandInfos; + } + + public void setCommandInfos(CommandInfo[] commandInfos) { + this.commandInfos = commandInfos; + } } diff --git a/core/src/main/java/com/cloud/agent/api/PrepareExternalProvisioningAnswer.java b/core/src/main/java/com/cloud/agent/api/PrepareExternalProvisioningAnswer.java new file mode 100644 index 000000000000..b94d18c537ea --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/PrepareExternalProvisioningAnswer.java @@ -0,0 +1,51 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +package com.cloud.agent.api; + +import java.util.Map; + +import com.cloud.agent.api.to.VirtualMachineTO; + +public class PrepareExternalProvisioningAnswer extends Answer { + + Map serverDetails; + VirtualMachineTO virtualMachineTO; + + public PrepareExternalProvisioningAnswer() { + super(); + } + + public PrepareExternalProvisioningAnswer(PrepareExternalProvisioningCommand cmd, Map externalDetails, VirtualMachineTO virtualMachineTO, String details) { + super(cmd, true, details); + this.serverDetails = externalDetails; + this.virtualMachineTO = virtualMachineTO; + } + + public PrepareExternalProvisioningAnswer(PrepareExternalProvisioningCommand cmd, boolean success, String details) { + super(cmd, success, details); + } + + public Map getServerDetails() { + return serverDetails; + } + + public VirtualMachineTO getVirtualMachineTO() { + return virtualMachineTO; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/PrepareExternalProvisioningCommand.java b/core/src/main/java/com/cloud/agent/api/PrepareExternalProvisioningCommand.java new file mode 100644 index 000000000000..44f57607eba9 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/PrepareExternalProvisioningCommand.java @@ -0,0 +1,39 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +package com.cloud.agent.api; + +import com.cloud.agent.api.to.VirtualMachineTO; + +public class PrepareExternalProvisioningCommand extends Command { + + VirtualMachineTO virtualMachineTO; + + public PrepareExternalProvisioningCommand(VirtualMachineTO vmTO) { + this.virtualMachineTO = vmTO; + } + + public VirtualMachineTO getVirtualMachineTO() { + return virtualMachineTO; + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/PrepareStorageClientAnswer.java b/core/src/main/java/com/cloud/agent/api/PrepareStorageClientAnswer.java new file mode 100644 index 000000000000..85afb9256464 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/PrepareStorageClientAnswer.java @@ -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. +// + +package com.cloud.agent.api; + +import java.util.Map; + +public class PrepareStorageClientAnswer extends Answer { + Map detailsMap; + + public PrepareStorageClientAnswer() { + super(); + } + + public PrepareStorageClientAnswer(Command command, boolean success, Map detailsMap) { + super(command, success, ""); + this.detailsMap = detailsMap; + } + + public PrepareStorageClientAnswer(Command command, boolean success, String details) { + super(command, success, details); + } + + public Map getDetailsMap() { + return detailsMap; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/PrepareStorageClientCommand.java b/core/src/main/java/com/cloud/agent/api/PrepareStorageClientCommand.java new file mode 100644 index 000000000000..8dea9c11c535 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/PrepareStorageClientCommand.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 com.cloud.agent.api; + +import java.util.Map; + +import com.cloud.storage.Storage.StoragePoolType; + +public class PrepareStorageClientCommand extends Command { + private StoragePoolType poolType; + private String poolUuid; + private Map details; + + public PrepareStorageClientCommand() { + } + + public PrepareStorageClientCommand(StoragePoolType poolType, String poolUuid, Map details) { + this.poolType = poolType; + this.poolUuid = poolUuid; + this.details = details; + } + + @Override + public boolean executeInSequence() { + return false; + } + + public StoragePoolType getPoolType() { + return poolType; + } + + public String getPoolUuid() { + return poolUuid; + } + + public Map getDetails() { + return details; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/ReadyCommand.java b/core/src/main/java/com/cloud/agent/api/ReadyCommand.java index 637e4f54da06..ee61fee66c63 100644 --- a/core/src/main/java/com/cloud/agent/api/ReadyCommand.java +++ b/core/src/main/java/com/cloud/agent/api/ReadyCommand.java @@ -19,30 +19,38 @@ package com.cloud.agent.api; +import com.cloud.host.Host; + import java.util.List; public class ReadyCommand extends Command { private String _details; - public ReadyCommand() { - super(); - } - private Long dcId; private Long hostId; + private String hostUuid; + private String hostName; private List msHostList; + private List avoidMsHostList; private String lbAlgorithm; private Long lbCheckInterval; private Boolean enableHumanReadableSizes; + private String arch; + + public ReadyCommand() { + super(); + } public ReadyCommand(Long dcId) { super(); this.dcId = dcId; } - public ReadyCommand(final Long dcId, final Long hostId, boolean enableHumanReadableSizes) { - this(dcId); - this.hostId = hostId; + public ReadyCommand(final Host host, boolean enableHumanReadableSizes) { + this(host.getDataCenterId()); + this.hostId = host.getId(); + this.hostUuid = host.getUuid(); + this.hostName = host.getName(); this.enableHumanReadableSizes = enableHumanReadableSizes; } @@ -67,6 +75,14 @@ public Long getHostId() { return hostId; } + public String getHostUuid() { + return hostUuid; + } + + public String getHostName() { + return hostName; + } + public List getMsHostList() { return msHostList; } @@ -75,6 +91,14 @@ public void setMsHostList(List msHostList) { this.msHostList = msHostList; } + public List getAvoidMsHostList() { + return avoidMsHostList; + } + + public void setAvoidMsHostList(List avoidMsHostList) { + this.avoidMsHostList = avoidMsHostList; + } + public String getLbAlgorithm() { return lbAlgorithm; } @@ -94,4 +118,12 @@ public void setLbCheckInterval(Long lbCheckInterval) { public Boolean getEnableHumanReadableSizes() { return enableHumanReadableSizes; } + + public String getArch() { + return arch; + } + + public void setArch(String arch) { + this.arch = arch; + } } diff --git a/core/src/main/java/com/cloud/agent/api/RecreateCheckpointsCommand.java b/core/src/main/java/com/cloud/agent/api/RecreateCheckpointsCommand.java new file mode 100644 index 000000000000..154b611be98f --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/RecreateCheckpointsCommand.java @@ -0,0 +1,49 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package com.cloud.agent.api; + +import org.apache.cloudstack.storage.to.VolumeObjectTO; + +import java.util.List; + +public class RecreateCheckpointsCommand extends Command { + + private List volumes; + + private String vmName; + + public RecreateCheckpointsCommand(List volumes, String vmName) { + this.volumes = volumes; + this.vmName = vmName; + } + + public List getDisks() { + return volumes; + } + + public String getVmName() { + return vmName; + } + + @Override + public boolean executeInSequence() { + return true; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/RemoveBitmapCommand.java b/core/src/main/java/com/cloud/agent/api/RemoveBitmapCommand.java new file mode 100644 index 000000000000..90b62e3f17b9 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/RemoveBitmapCommand.java @@ -0,0 +1,46 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +package com.cloud.agent.api; + +import org.apache.cloudstack.storage.to.SnapshotObjectTO; + +public class RemoveBitmapCommand extends Command { + + private SnapshotObjectTO snapshotObjectTO; + + private boolean isVmRunning; + + public RemoveBitmapCommand(SnapshotObjectTO snapshotObjectTO, boolean isVmRunning) { + this.snapshotObjectTO = snapshotObjectTO; + this.isVmRunning = isVmRunning; + } + + @Override + public boolean executeInSequence() { + return true; + } + + public SnapshotObjectTO getSnapshotObjectTO() { + return snapshotObjectTO; + } + + public boolean isVmRunning() { + return isVmRunning; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/RunCustomActionAnswer.java b/core/src/main/java/com/cloud/agent/api/RunCustomActionAnswer.java new file mode 100644 index 000000000000..1deb789c995f --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/RunCustomActionAnswer.java @@ -0,0 +1,32 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package com.cloud.agent.api; + +public class RunCustomActionAnswer extends Answer { + + public RunCustomActionAnswer(RunCustomActionCommand cmd, boolean success, String details) { + super(cmd, success, details); + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/RunCustomActionCommand.java b/core/src/main/java/com/cloud/agent/api/RunCustomActionCommand.java new file mode 100644 index 000000000000..113073ac5ee5 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/RunCustomActionCommand.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 java.util.Map; + +import com.cloud.agent.api.to.VirtualMachineTO; + +public class RunCustomActionCommand extends Command { + + String actionName; + VirtualMachineTO vmTO; + Map parameters; + + public RunCustomActionCommand(String actionName) { + this.actionName = actionName; + this.setWait(5); + } + + public String getActionName() { + return actionName; + } + + public VirtualMachineTO getVmTO() { + return vmTO; + } + + public void setVmTO(VirtualMachineTO vmTO) { + this.vmTO = vmTO; + } + + public Map getParameters() { + return parameters; + } + + public void setParameters(Map parameters) { + this.parameters = parameters; + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/SetupGuestNetworkCommand.java b/core/src/main/java/com/cloud/agent/api/SetupGuestNetworkCommand.java index e9781993239b..fef0ceed6b85 100644 --- a/core/src/main/java/com/cloud/agent/api/SetupGuestNetworkCommand.java +++ b/core/src/main/java/com/cloud/agent/api/SetupGuestNetworkCommand.java @@ -35,6 +35,8 @@ public class SetupGuestNetworkCommand extends NetworkElementCommand { String routerIpv6 = null; String routerIpv6Gateway = null; String routerIpv6Cidr = null; + boolean isVrGuestGateway = false; + long networkId; public NicTO getNic() { return nic; @@ -114,4 +116,20 @@ public void setDefaultIp6Dns1(String defaultIp6Dns1) { public void setDefaultIp6Dns2(String defaultIp6Dns2) { this.defaultIp6Dns2 = defaultIp6Dns2; } + + public boolean isVrGuestGateway() { + return isVrGuestGateway; + } + + public void setVrGuestGateway(boolean vrGuestGateway) { + isVrGuestGateway = vrGuestGateway; + } + + public long getNetworkId() { + return networkId; + } + + public void setNetworkId(long networkId) { + this.networkId = networkId; + } } diff --git a/core/src/main/java/com/cloud/agent/api/StartupAnswer.java b/core/src/main/java/com/cloud/agent/api/StartupAnswer.java index 71652269b66f..c619ce75ace6 100644 --- a/core/src/main/java/com/cloud/agent/api/StartupAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/StartupAnswer.java @@ -21,14 +21,18 @@ public class StartupAnswer extends Answer { long hostId; + String hostName; + String hostUuid; int pingInterval; protected StartupAnswer() { } - public StartupAnswer(StartupCommand cmd, long hostId, int pingInterval) { + public StartupAnswer(StartupCommand cmd, long hostId, String hostUuid, String hostName, int pingInterval) { super(cmd); this.hostId = hostId; + this.hostUuid = hostUuid; + this.hostName = hostName; this.pingInterval = pingInterval; } @@ -40,6 +44,14 @@ public long getHostId() { return hostId; } + public String getHostUuid() { + return hostUuid; + } + + public String getHostName() { + return hostName; + } + public int getPingInterval() { return pingInterval; } diff --git a/core/src/main/java/com/cloud/agent/api/StartupCommand.java b/core/src/main/java/com/cloud/agent/api/StartupCommand.java index 5f2c00d0be63..7a18ba2dccc1 100644 --- a/core/src/main/java/com/cloud/agent/api/StartupCommand.java +++ b/core/src/main/java/com/cloud/agent/api/StartupCommand.java @@ -47,6 +47,8 @@ public class StartupCommand extends Command { String resourceName; String gatewayIpAddress; String msHostList; + boolean connectionTransferred; + String arch; public StartupCommand(Host.Type type) { this.type = type; @@ -290,6 +292,22 @@ public void setMSHostList(String msHostList) { this.msHostList = msHostList; } + public boolean isConnectionTransferred() { + return connectionTransferred; + } + + public void setConnectionTransferred(boolean connectionTransferred) { + this.connectionTransferred = connectionTransferred; + } + + public String getArch() { + return arch; + } + + public void setArch(String arch) { + this.arch = arch; + } + @Override public boolean executeInSequence() { return false; diff --git a/core/src/main/java/com/cloud/agent/api/StartupRoutingCommand.java b/core/src/main/java/com/cloud/agent/api/StartupRoutingCommand.java index b4f9d20df5ed..068196aabe54 100644 --- a/core/src/main/java/com/cloud/agent/api/StartupRoutingCommand.java +++ b/core/src/main/java/com/cloud/agent/api/StartupRoutingCommand.java @@ -32,6 +32,7 @@ public class StartupRoutingCommand extends StartupCommand { Integer cpuSockets; int cpus; long speed; + String cpuArch; long memory; long dom0MinMemory; boolean poolSync; @@ -44,6 +45,7 @@ public class StartupRoutingCommand extends StartupCommand { List hostTags = new ArrayList(); String hypervisorVersion; HashMap> groupDetails = new HashMap>(); + List gpuDevices = new ArrayList<>(); private Boolean hostHealthCheckResult; public StartupRoutingCommand() { @@ -174,7 +176,11 @@ public void setHostTags(String hostTag) { this.hostTags.add(hostTag); } - public HashMap> getGpuGroupDetails() { + public void setHostTags(List hostTags) { + this.hostTags = hostTags; + } + + public HashMap> getGpuGroupDetails() { return groupDetails; } @@ -182,6 +188,14 @@ public void setGpuGroupDetails(HashMap> g this.groupDetails = groupDetails; } + public List getGpuDevices() { + return gpuDevices; + } + + public void setGpuDevices(List gpuDevices) { + this.gpuDevices = gpuDevices; + } + public boolean getSupportsClonedVolumes() { return supportsClonedVolumes; } @@ -197,4 +211,12 @@ public Boolean getHostHealthCheckResult() { public void setHostHealthCheckResult(Boolean hostHealthCheckResult) { this.hostHealthCheckResult = hostHealthCheckResult; } + + public String getCpuArch() { + return cpuArch; + } + + public void setCpuArch(String cpuArch) { + this.cpuArch = cpuArch; + } } diff --git a/core/src/main/java/com/cloud/agent/api/StopCommand.java b/core/src/main/java/com/cloud/agent/api/StopCommand.java index 3923a35bd0a7..d07ffa2e31f7 100644 --- a/core/src/main/java/com/cloud/agent/api/StopCommand.java +++ b/core/src/main/java/com/cloud/agent/api/StopCommand.java @@ -19,14 +19,14 @@ package com.cloud.agent.api; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + import com.cloud.agent.api.to.DpdkTO; import com.cloud.agent.api.to.GPUDeviceTO; import com.cloud.vm.VirtualMachine; -import java.util.ArrayList; -import java.util.Map; -import java.util.List; - public class StopCommand extends RebootCommand { private boolean isProxy = false; private String urlPort = null; @@ -37,6 +37,7 @@ public class StopCommand extends RebootCommand { boolean forceStop = false; private Map dpdkInterfaceMapping; Map vlanToPersistenceMap; + boolean expungeVM = false; public Map getDpdkInterfaceMapping() { return dpdkInterfaceMapping; @@ -138,4 +139,12 @@ public Map getVlanToPersistenceMap() { public void setVlanToPersistenceMap(Map vlanToPersistenceMap) { this.vlanToPersistenceMap = vlanToPersistenceMap; } + + public boolean isExpungeVM() { + return expungeVM; + } + + public void setExpungeVM(boolean expungeVM) { + this.expungeVM = expungeVM; + } } diff --git a/core/src/main/java/com/cloud/agent/api/TransferAgentCommand.java b/core/src/main/java/com/cloud/agent/api/TransferAgentCommand.java index ab74d9bcf852..9c6b3b5fc592 100644 --- a/core/src/main/java/com/cloud/agent/api/TransferAgentCommand.java +++ b/core/src/main/java/com/cloud/agent/api/TransferAgentCommand.java @@ -25,6 +25,7 @@ public class TransferAgentCommand extends Command { protected long agentId; protected long futureOwner; protected long currentOwner; + protected boolean isConnectionTransfer; Event event; protected TransferAgentCommand() { @@ -37,6 +38,11 @@ public TransferAgentCommand(long agentId, long currentOwner, long futureOwner, E this.event = event; } + public TransferAgentCommand(long agentId, long currentOwner, long futureOwner, Event event, boolean isConnectionTransfer) { + this(agentId, currentOwner, futureOwner, event); + this.isConnectionTransfer = isConnectionTransfer; + } + public long getAgentId() { return agentId; } @@ -53,6 +59,10 @@ public long getCurrentOwner() { return currentOwner; } + public boolean isConnectionTransfer() { + return isConnectionTransfer; + } + @Override public boolean executeInSequence() { return false; 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/UnprepareStorageClientAnswer.java b/core/src/main/java/com/cloud/agent/api/UnprepareStorageClientAnswer.java new file mode 100644 index 000000000000..1280293db0dc --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/UnprepareStorageClientAnswer.java @@ -0,0 +1,34 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package com.cloud.agent.api; + +public class UnprepareStorageClientAnswer extends Answer { + public UnprepareStorageClientAnswer() { + super(); + } + + public UnprepareStorageClientAnswer(Command command, boolean success) { + super(command, success, ""); + } + + public UnprepareStorageClientAnswer(Command command, boolean success, String details) { + super(command, success, details); + } +} diff --git a/core/src/main/java/com/cloud/agent/api/UnprepareStorageClientCommand.java b/core/src/main/java/com/cloud/agent/api/UnprepareStorageClientCommand.java new file mode 100644 index 000000000000..b98ce6fa345e --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/UnprepareStorageClientCommand.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 com.cloud.agent.api; + +import java.util.Map; + +import com.cloud.storage.Storage.StoragePoolType; + +public class UnprepareStorageClientCommand extends Command { + private StoragePoolType poolType; + private String poolUuid; + private Map details; + + public UnprepareStorageClientCommand() { + } + + public UnprepareStorageClientCommand(StoragePoolType poolType, String poolUuid, Map details) { + this.poolType = poolType; + this.poolUuid = poolUuid; + this.details = details; + } + + @Override + public boolean executeInSequence() { + return false; + } + + public StoragePoolType getPoolType() { + return poolType; + } + + public String getPoolUuid() { + return poolUuid; + } + + public Map getDetails() { + return details; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/routing/SetBgpPeersAnswer.java b/core/src/main/java/com/cloud/agent/api/routing/SetBgpPeersAnswer.java new file mode 100644 index 000000000000..9645b300db59 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/routing/SetBgpPeersAnswer.java @@ -0,0 +1,46 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package com.cloud.agent.api.routing; + +import java.util.Arrays; + +import com.cloud.agent.api.Answer; + +public class SetBgpPeersAnswer extends Answer { + String[] results; + + protected SetBgpPeersAnswer() { + } + + public SetBgpPeersAnswer(SetBgpPeersCommand cmd, boolean success, String[] results) { + super(cmd, success, null); + if (results != null) { + assert (cmd.getBpgPeers().length == results.length) : "BGP peers and their results should be the same length"; + this.results = Arrays.copyOf(results, results.length); + } + } + + public String[] getResults() { + if (results != null) { + return Arrays.copyOf(results, results.length); + } + return null; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/routing/SetBgpPeersCommand.java b/core/src/main/java/com/cloud/agent/api/routing/SetBgpPeersCommand.java new file mode 100644 index 000000000000..36371a196c81 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/routing/SetBgpPeersCommand.java @@ -0,0 +1,39 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package com.cloud.agent.api.routing; + +import java.util.List; + +import org.apache.cloudstack.network.BgpPeerTO; + +public class SetBgpPeersCommand extends NetworkElementCommand { + BgpPeerTO[] bpgPeers; + + protected SetBgpPeersCommand() { + } + + public SetBgpPeersCommand(List bpgPeers) { + this.bpgPeers = bpgPeers.toArray(new BgpPeerTO[bpgPeers.size()]); + } + + public BgpPeerTO[] getBpgPeers() { + return bpgPeers; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/storage/CheckAndRepairVolumeAnswer.java b/core/src/main/java/com/cloud/agent/api/storage/CheckAndRepairVolumeAnswer.java new file mode 100644 index 000000000000..3dc7752bfefc --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/storage/CheckAndRepairVolumeAnswer.java @@ -0,0 +1,57 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package com.cloud.agent.api.storage; + +import com.cloud.agent.api.Answer; + +public class CheckAndRepairVolumeAnswer extends Answer { + private String volumeCheckExecutionResult; + private String volumeRepairExecutionResult; + + protected CheckAndRepairVolumeAnswer() { + super(); + } + + public CheckAndRepairVolumeAnswer(CheckAndRepairVolumeCommand cmd, boolean result, String details, String volumeCheckExecutionResult, String volumeRepairedExecutionResult) { + super(cmd, result, details); + this.volumeCheckExecutionResult = volumeCheckExecutionResult; + this.volumeRepairExecutionResult = volumeRepairedExecutionResult; + } + + public CheckAndRepairVolumeAnswer(CheckAndRepairVolumeCommand cmd, boolean result, String details) { + super(cmd, result, details); + } + + public String getVolumeCheckExecutionResult() { + return volumeCheckExecutionResult; + } + + public String getVolumeRepairExecutionResult() { + return volumeRepairExecutionResult; + } + + public void setVolumeCheckExecutionResult(String volumeCheckExecutionResult) { + this.volumeCheckExecutionResult = volumeCheckExecutionResult; + } + + public void setVolumeRepairExecutionResult(String volumeRepairExecutionResult) { + this.volumeRepairExecutionResult = volumeRepairExecutionResult; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/storage/CheckAndRepairVolumeCommand.java b/core/src/main/java/com/cloud/agent/api/storage/CheckAndRepairVolumeCommand.java new file mode 100644 index 000000000000..2553fdf477c5 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/storage/CheckAndRepairVolumeCommand.java @@ -0,0 +1,77 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package com.cloud.agent.api.storage; + +import com.cloud.agent.api.Command; +import com.cloud.agent.api.LogLevel; +import com.cloud.agent.api.to.StorageFilerTO; + +import java.util.Arrays; + +public class CheckAndRepairVolumeCommand extends Command { + private String path; + private StorageFilerTO pool; + private String repair; + @LogLevel(LogLevel.Log4jLevel.Off) + private byte[] passphrase; + private String encryptFormat; + + public CheckAndRepairVolumeCommand(String path, StorageFilerTO pool, String repair, byte[] passphrase, String encryptFormat) { + this.path = path; + this.pool = pool; + this.repair = repair; + this.passphrase = passphrase; + this.encryptFormat = encryptFormat; + } + + public String getPath() { + return path; + } + + public String getPoolUuid() { + return pool.getUuid(); + } + + public StorageFilerTO getPool() { + return pool; + } + + public String getRepair() { + return repair; + } + + public String getEncryptFormat() { return encryptFormat; } + + public byte[] getPassphrase() { return passphrase; } + + public void clearPassphrase() { + if (this.passphrase != null) { + Arrays.fill(this.passphrase, (byte) 0); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotAnswer.java b/core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotAnswer.java new file mode 100644 index 000000000000..4d61249c7cbc --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotAnswer.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.cloud.agent.api.storage; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.utils.Pair; + +import java.util.Map; + +public class CreateDiskOnlyVmSnapshotAnswer extends Answer { + + protected Map> mapVolumeToSnapshotSizeAndNewVolumePath; + + public CreateDiskOnlyVmSnapshotAnswer(Command command, boolean success, String details, Map> mapVolumeToSnapshotSizeAndNewVolumePath) { + super(command, success, details); + this.mapVolumeToSnapshotSizeAndNewVolumePath = mapVolumeToSnapshotSizeAndNewVolumePath; + } + + public Map> getMapVolumeToSnapshotSizeAndNewVolumePath() { + return mapVolumeToSnapshotSizeAndNewVolumePath; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotCommand.java b/core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotCommand.java new file mode 100644 index 000000000000..952bf0c971de --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotCommand.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.cloud.agent.api.storage; + + +import com.cloud.agent.api.VMSnapshotBaseCommand; +import com.cloud.agent.api.VMSnapshotTO; +import com.cloud.vm.VirtualMachine; +import org.apache.cloudstack.storage.to.VolumeObjectTO; + +import java.util.List; + +public class CreateDiskOnlyVmSnapshotCommand extends VMSnapshotBaseCommand { + + protected VirtualMachine.State vmState; + + public CreateDiskOnlyVmSnapshotCommand(String vmName, VMSnapshotTO snapshot, List volumeTOs, String guestOSType, VirtualMachine.State vmState) { + super(vmName, snapshot, volumeTOs, guestOSType); + this.vmState = vmState; + } + + public VirtualMachine.State getVmState() { + return vmState; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/storage/CreateEntityDownloadURLCommand.java b/core/src/main/java/com/cloud/agent/api/storage/CreateEntityDownloadURLCommand.java index c84eceb0ca2f..33403580b6fe 100644 --- a/core/src/main/java/com/cloud/agent/api/storage/CreateEntityDownloadURLCommand.java +++ b/core/src/main/java/com/cloud/agent/api/storage/CreateEntityDownloadURLCommand.java @@ -23,18 +23,19 @@ public class CreateEntityDownloadURLCommand extends AbstractDownloadCommand { - public CreateEntityDownloadURLCommand(String parent, String installPath, String uuid, DataTO data) { // this constructor is for creating template download url + public CreateEntityDownloadURLCommand(String parent, String installPath, String fileName, String filePath, DataTO data) { // this constructor is for creating template download url super(); this.parent = parent; // parent is required as not the template can be child of one of many parents this.installPath = installPath; - this.extractLinkUUID = uuid; + this.filenameInExtractURL = fileName; + this.filepathInExtractURL = filePath; this.data = data; } - public CreateEntityDownloadURLCommand(String installPath, String uuid) { + public CreateEntityDownloadURLCommand(String installPath, String filename) { super(); this.installPath = installPath; - this.extractLinkUUID = uuid; + this.filenameInExtractURL = filename; } public CreateEntityDownloadURLCommand() { @@ -42,7 +43,8 @@ public CreateEntityDownloadURLCommand() { private String installPath; private String parent; - private String extractLinkUUID; + private String filenameInExtractURL; + private String filepathInExtractURL; public DataTO getData() { return data; @@ -75,12 +77,19 @@ public void setParent(String parent) { this.parent = parent; } - public String getExtractLinkUUID() { - return extractLinkUUID; + public String getFilenameInExtractURL() { + return filenameInExtractURL; } - public void setExtractLinkUUID(String extractLinkUUID) { - this.extractLinkUUID = extractLinkUUID; + public void setFilenameInExtractURL(String filenameInExtractURL) { + this.filenameInExtractURL = filenameInExtractURL; } + public String getFilepathInExtractURL() { + return filepathInExtractURL; + } + + public void setFilepathInExtractURL(String filepathInExtractURL) { + this.filepathInExtractURL = filepathInExtractURL; + } } diff --git a/core/src/main/java/com/cloud/agent/api/storage/DeleteDiskOnlyVmSnapshotCommand.java b/core/src/main/java/com/cloud/agent/api/storage/DeleteDiskOnlyVmSnapshotCommand.java new file mode 100644 index 000000000000..bf7bdd597360 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/storage/DeleteDiskOnlyVmSnapshotCommand.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.cloud.agent.api.storage; + +import com.cloud.agent.api.Command; + +import com.cloud.agent.api.to.DataTO; + + +import java.util.List; + +public class DeleteDiskOnlyVmSnapshotCommand extends Command { + + List snapshots; + + public DeleteDiskOnlyVmSnapshotCommand(List snapshots) { + this.snapshots = snapshots; + } + + public List getSnapshots() { + return snapshots; + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/storage/MergeDiskOnlyVmSnapshotCommand.java b/core/src/main/java/com/cloud/agent/api/storage/MergeDiskOnlyVmSnapshotCommand.java new file mode 100644 index 000000000000..b6396c24d10a --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/storage/MergeDiskOnlyVmSnapshotCommand.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.cloud.agent.api.storage; + +import com.cloud.agent.api.Command; +import com.cloud.vm.VirtualMachine; + +import java.util.List; + +public class MergeDiskOnlyVmSnapshotCommand extends Command { + + private List snapshotMergeTreeToList; + private VirtualMachine.State vmState; + private String vmName; + + public MergeDiskOnlyVmSnapshotCommand(List snapshotMergeTreeToList, VirtualMachine.State vmState, String vmName) { + this.snapshotMergeTreeToList = snapshotMergeTreeToList; + this.vmState = vmState; + this.vmName = vmName; + } + + public List getSnapshotMergeTreeToList() { + return snapshotMergeTreeToList; + } + + public VirtualMachine.State getVmState() { + return vmState; + } + + public String getVmName() { + return vmName; + } + + @Override + public boolean executeInSequence() { + return false; + } + +} diff --git a/core/src/main/java/com/cloud/agent/api/storage/MigrateVolumeCommand.java b/core/src/main/java/com/cloud/agent/api/storage/MigrateVolumeCommand.java index 9fed0f913e14..70375c30a1bb 100644 --- a/core/src/main/java/com/cloud/agent/api/storage/MigrateVolumeCommand.java +++ b/core/src/main/java/com/cloud/agent/api/storage/MigrateVolumeCommand.java @@ -145,4 +145,9 @@ public int getWaitInMillSeconds() { } public String getChainInfo() { return chainInfo; } + + @Override + public boolean isReconcile() { + return true; + } } diff --git a/core/src/main/java/com/cloud/agent/api/storage/RevertDiskOnlyVmSnapshotAnswer.java b/core/src/main/java/com/cloud/agent/api/storage/RevertDiskOnlyVmSnapshotAnswer.java new file mode 100644 index 000000000000..2ecf587d59d1 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/storage/RevertDiskOnlyVmSnapshotAnswer.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.cloud.agent.api.storage; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import org.apache.cloudstack.storage.to.VolumeObjectTO; + +import java.util.List; + +public class RevertDiskOnlyVmSnapshotAnswer extends Answer { + List volumeObjectTos; + + public RevertDiskOnlyVmSnapshotAnswer(Command cmd, List volumeObjectTos) { + super(cmd, true, null); + this.volumeObjectTos = volumeObjectTos; + } + + public List getVolumeObjectTos() { + return volumeObjectTos; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/storage/RevertDiskOnlyVmSnapshotCommand.java b/core/src/main/java/com/cloud/agent/api/storage/RevertDiskOnlyVmSnapshotCommand.java new file mode 100644 index 000000000000..72bb92bcb10d --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/storage/RevertDiskOnlyVmSnapshotCommand.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.cloud.agent.api.storage; + +import com.cloud.agent.api.Command; +import org.apache.cloudstack.storage.to.SnapshotObjectTO; + +import java.util.List; + +public class RevertDiskOnlyVmSnapshotCommand extends Command { + + private List snapshotObjectTos; + private String vmName; + + public RevertDiskOnlyVmSnapshotCommand(List snapshotObjectTos, String vmName) { + super(); + this.snapshotObjectTos = snapshotObjectTos; + this.vmName = vmName; + } + + public List getSnapshotObjectTos() { + return snapshotObjectTos; + } + + public String getVmName() { + return vmName; + } + + @Override + public boolean executeInSequence() { + return false; + } + +} diff --git a/core/src/main/java/com/cloud/agent/api/storage/SnapshotMergeTreeTO.java b/core/src/main/java/com/cloud/agent/api/storage/SnapshotMergeTreeTO.java new file mode 100644 index 000000000000..78f23105e192 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/storage/SnapshotMergeTreeTO.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.cloud.agent.api.storage; + +import com.cloud.agent.api.to.DataTO; +import org.apache.commons.lang3.builder.ReflectionToStringBuilder; + +import java.util.List; + +public class SnapshotMergeTreeTO { + DataTO parent; + DataTO child; + List grandChildren; + + public SnapshotMergeTreeTO(DataTO parent, DataTO child, List grandChildren) { + this.parent = parent; + this.child = child; + this.grandChildren = grandChildren; + } + + public DataTO getParent() { + return parent; + } + + public DataTO getChild() { + return child; + } + + public List getGrandChildren() { + return grandChildren; + } + + public void addGrandChild(DataTO grandChild) { + grandChildren.add(grandChild); + } + + @Override + public String toString() { + return ReflectionToStringBuilder.toString(this); + } +} diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java index ebe5e9a7ec94..7bfbf786e9b4 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java @@ -41,6 +41,7 @@ public class VRScripts { public static final String DHCP_CONFIG = "dhcp.json"; public static final String IP_ALIAS_CONFIG = "ip_aliases.json"; public static final String LOAD_BALANCER_CONFIG = "load_balancer.json"; + public static final String BGP_PEERS_CONFIG = "bgp_peers.json"; public static final String SYSTEM_VM_PATCHED = "patched.sh"; public final static String CONFIG_CACHE_LOCATION = "/var/cache/cloud/"; @@ -81,4 +82,8 @@ public class VRScripts { public static final String VR_UPDATE_INTERFACE_CONFIG = "update_interface_config.sh"; public static final String ROUTER_FILESYSTEM_WRITABLE_CHECK = "filesystem_writable_check.py"; + + // CKS ISO mount + public static final String CKS_ISO_MOUNT_SERVE = "cks_iso.sh"; + public static final String MANAGE_SERVICE = "manage_service.sh"; } diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java index 3c86b3a0dcc4..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 @@ -34,6 +34,8 @@ import javax.naming.ConfigurationException; +import com.cloud.agent.api.HandleCksIsoCommand; +import org.apache.cloudstack.agent.routing.ManageServiceCommand; import com.cloud.agent.api.routing.UpdateNetworkCommand; import com.cloud.agent.api.to.IpAddressTO; import com.cloud.network.router.VirtualRouter; @@ -144,6 +146,14 @@ public Answer executeRequest(final NetworkElementCommand cmd) { return execute((UpdateNetworkCommand) cmd); } + if (cmd instanceof HandleCksIsoCommand) { + return execute((HandleCksIsoCommand) cmd); + } + + if (cmd instanceof ManageServiceCommand) { + return execute((ManageServiceCommand) cmd); + } + if (_vrAggregateCommandsSet.containsKey(routerName)) { _vrAggregateCommandsSet.get(routerName).add(cmd); aggregated = true; @@ -171,6 +181,13 @@ public Answer executeRequest(final NetworkElementCommand cmd) { } } + protected Answer execute(final HandleCksIsoCommand cmd) { + String routerIp = getRouterSshControlIp(cmd); + logger.info("Attempting to mount CKS ISO on Virtual Router"); + ExecutionResult result = _vrDeployer.executeInVR(routerIp, VRScripts.CKS_ISO_MOUNT_SERVE, String.valueOf(cmd.isMountCksIso())); + return new Answer(cmd, result.isSuccess(), result.getDetails()); + } + private Answer execute(final SetupKeyStoreCommand cmd) { final String args = String.format("/usr/local/cloud/systemvm/conf/agent.properties " + "/usr/local/cloud/systemvm/conf/%s " + @@ -250,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) { @@ -271,6 +288,20 @@ private Answer execute(UpdateNetworkCommand cmd) { return new Answer(cmd, new CloudRuntimeException("Failed to update interface mtu")); } + private Answer execute(ManageServiceCommand cmd) { + String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP); + String args = cmd.getAction() + " " + cmd.getServiceName(); + ExecutionResult result = _vrDeployer.executeInVR(routerIp, VRScripts.MANAGE_SERVICE, args); + if (result.isSuccess()) { + return new Answer(cmd, true, + String.format("Successfully executed action: %s on service: %s. Details: %s", + cmd.getAction(), cmd.getServiceName(), result.getDetails())); + } else { + return new Answer(cmd, false, String.format("Failed to execute action: %s on service: %s. Details: %s", + cmd.getAction(), cmd.getServiceName(), result.getDetails())); + } + } + private ExecutionResult applyConfigToVR(String routerAccessIp, ConfigItem c) { return applyConfigToVR(routerAccessIp, c, VRScripts.VR_SCRIPT_EXEC_TIMEOUT); } diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/AbstractConfigItemFacade.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/AbstractConfigItemFacade.java index 46dd801bebf5..83dfa2a62caa 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/AbstractConfigItemFacade.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/AbstractConfigItemFacade.java @@ -37,6 +37,7 @@ import com.cloud.agent.api.routing.NetworkElementCommand; import com.cloud.agent.api.routing.RemoteAccessVpnCfgCommand; import com.cloud.agent.api.routing.SavePasswordCommand; +import com.cloud.agent.api.routing.SetBgpPeersCommand; import com.cloud.agent.api.routing.SetFirewallRulesCommand; import com.cloud.agent.api.routing.SetIpv6FirewallRulesCommand; import com.cloud.agent.api.routing.SetMonitorServiceCommand; @@ -98,6 +99,7 @@ public abstract class AbstractConfigItemFacade { flyweight.put(SetSourceNatCommand.class, new SetSourceNatConfigItem()); flyweight.put(IpAssocCommand.class, new IpAssociationConfigItem()); flyweight.put(IpAssocVpcCommand.class, new IpAssociationConfigItem()); + flyweight.put(SetBgpPeersCommand.class, new SetBgpPeersConfigItem()); } protected String destinationFile; diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/LoadBalancerConfigItem.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/LoadBalancerConfigItem.java index 4832c906699e..6dae886b4137 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/LoadBalancerConfigItem.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/LoadBalancerConfigItem.java @@ -56,6 +56,8 @@ public List generateConfig(final NetworkElementCommand cmd) { final String[] statRules = allRules[LoadBalancerConfigurator.STATS]; final LoadBalancerRule loadBalancerRule = new LoadBalancerRule(configuration, tmpCfgFilePath, tmpCfgFileName, addRules, removeRules, statRules, routerIp); + final LoadBalancerRule.SslCertEntry[] sslCerts = cfgtr.generateSslCertEntries(command); + loadBalancerRule.setSslCerts(sslCerts); final List rules = new LinkedList(); rules.add(loadBalancerRule); diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetBgpPeersConfigItem.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetBgpPeersConfigItem.java new file mode 100644 index 000000000000..68f4275bb6be --- /dev/null +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetBgpPeersConfigItem.java @@ -0,0 +1,46 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package com.cloud.agent.resource.virtualnetwork.facade; + +import java.util.Arrays; +import java.util.List; + +import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.agent.api.routing.SetBgpPeersCommand; +import com.cloud.agent.resource.virtualnetwork.ConfigItem; +import com.cloud.agent.resource.virtualnetwork.VRScripts; +import com.cloud.agent.resource.virtualnetwork.model.BgpPeers; +import com.cloud.agent.resource.virtualnetwork.model.ConfigBase; + +public class SetBgpPeersConfigItem extends AbstractConfigItemFacade { + + @Override + public List generateConfig(final NetworkElementCommand cmd) { + final SetBgpPeersCommand command = (SetBgpPeersCommand) cmd; + return generateConfigItems(new BgpPeers(Arrays.asList(command.getBpgPeers()))); + } + + @Override + protected List generateConfigItems(final ConfigBase configuration) { + destinationFile = VRScripts.BGP_PEERS_CONFIG; + + return super.generateConfigItems(configuration); + } +} diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetGuestNetworkConfigItem.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetGuestNetworkConfigItem.java index aee1e7795712..57c96ec4bc95 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetGuestNetworkConfigItem.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetGuestNetworkConfigItem.java @@ -75,6 +75,8 @@ public List generateConfig(final NetworkElementCommand cmd) { guestNetwork.setRouterIp6(command.getRouterIpv6()); guestNetwork.setRouterIp6Gateway(command.getRouterIpv6Gateway()); guestNetwork.setRouterIp6Cidr(command.getRouterIpv6Cidr()); + guestNetwork.setVrGuestGateway(command.isVrGuestGateway()); + guestNetwork.setNetworkId(command.getNetworkId()); return generateConfigItems(guestNetwork); } diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetPortForwardingRulesConfigItem.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetPortForwardingRulesConfigItem.java index c9d0e74e2e40..4daef64ed8a4 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetPortForwardingRulesConfigItem.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetPortForwardingRulesConfigItem.java @@ -41,7 +41,7 @@ public List generateConfig(final NetworkElementCommand cmd) { for (final PortForwardingRuleTO rule : command.getRules()) { final ForwardingRule fwdRule = new ForwardingRule(rule.revoked(), rule.getProtocol().toLowerCase(), rule.getSrcIp(), rule.getStringSrcPortRange(), rule.getDstIp(), - rule.getStringDstPortRange()); + rule.getStringDstPortRange(), rule.getSourceCidrListAsString()); rules.add(fwdRule); } diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/BgpPeers.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/BgpPeers.java new file mode 100644 index 000000000000..54a1ab2e0915 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/BgpPeers.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.agent.resource.virtualnetwork.model; + +import org.apache.cloudstack.network.BgpPeerTO; + +import java.util.List; + +public class BgpPeers extends ConfigBase { + private List peers; + + public BgpPeers() { + super(ConfigBase.BGP_PEERS); + } + + public BgpPeers(List bgpPeers) { + super(ConfigBase.BGP_PEERS); + this.peers = bgpPeers; + } + + public List getPeers() { + return peers; + } + + public void setPeers(List bgpPeers) { + this.peers = bgpPeers; + } +} diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/ConfigBase.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/ConfigBase.java index ade80d71384f..e370b764f226 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/ConfigBase.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/ConfigBase.java @@ -39,6 +39,7 @@ public abstract class ConfigBase { public static final String MONITORSERVICE = "monitorservice"; public static final String DHCP_CONFIG = "dhcpconfig"; public static final String LOAD_BALANCER = "loadbalancer"; + public final static String BGP_PEERS = "bgppeers"; private String type = UNKNOWN; diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/ForwardingRule.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/ForwardingRule.java index cf3e43d1c018..1dc1cc855c4c 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/ForwardingRule.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/ForwardingRule.java @@ -26,18 +26,21 @@ public class ForwardingRule { private String sourcePortRange; private String destinationIpAddress; private String destinationPortRange; + private String sourceCidrList; public ForwardingRule() { // Empty constructor for (de)serialization } - public ForwardingRule(boolean revoke, String protocol, String sourceIpAddress, String sourcePortRange, String destinationIpAddress, String destinationPortRange) { + public ForwardingRule(boolean revoke, String protocol, String sourceIpAddress, String sourcePortRange, String destinationIpAddress, String destinationPortRange, + String sourceCidrList) { this.revoke = revoke; this.protocol = protocol; this.sourceIpAddress = sourceIpAddress; this.sourcePortRange = sourcePortRange; this.destinationIpAddress = destinationIpAddress; this.destinationPortRange = destinationPortRange; + this.sourceCidrList = sourceCidrList; } public boolean isRevoke() { @@ -88,4 +91,8 @@ public void setDestinationPortRange(String destinationPortRange) { this.destinationPortRange = destinationPortRange; } + public String getSourceCidrList() { + return sourceCidrList; + } + } diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/GuestNetwork.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/GuestNetwork.java index bb5e443c2e8c..6bf36d62bf9f 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/GuestNetwork.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/GuestNetwork.java @@ -37,6 +37,8 @@ public class GuestNetwork extends ConfigBase { private String routerIp6; private String routerIp6Gateway; private String routerIp6Cidr; + private boolean isVrGuestGateway; + long networkId; private Integer mtu; @@ -202,4 +204,20 @@ public void setMtu(Integer mtu) { public Integer getMtu() { return mtu; } + + public boolean isVrGuestGateway() { + return isVrGuestGateway; + } + + public void setVrGuestGateway(boolean vrGuestGateway) { + isVrGuestGateway = vrGuestGateway; + } + + public long getNetworkId() { + return networkId; + } + + public void setNetworkId(long networkId) { + this.networkId = networkId; + } } diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/LoadBalancerRule.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/LoadBalancerRule.java index 3743d608e6c2..361c4765cc52 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/LoadBalancerRule.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/LoadBalancerRule.java @@ -25,6 +25,7 @@ public class LoadBalancerRule { private String[] configuration; private String tmpCfgFilePath; private String tmpCfgFileName; + private SslCertEntry[] sslCerts; private String[] addRules; private String[] removeRules; @@ -32,6 +33,53 @@ public class LoadBalancerRule { private String routerIp; + public static class SslCertEntry { + private String name; + private String cert; + private String key; + private String chain; + private String password; + + public SslCertEntry(String name, String cert, String key, String chain, String password) { + this.name = name; + this.cert = cert; + this.key = key; + this.chain = chain; + this.password = password; + } + + public void setName(String name) { + this.name = name; + } + public String getName() { + return name; + } + public void setCert(String cert) { + this.cert = cert; + } + public String getCert() { + return cert; + } + public void setKey(String key) { + this.key = key; + } + public String getKey() { + return key; + } + public void setChain(String chain) { + this.chain = chain; + } + public String getChain() { + return chain; + } + public void setPassword(String password) { + this.password = password; + } + public String getPassword() { + return password; + } + } + public LoadBalancerRule() { // Empty constructor for (de)serialization } @@ -101,4 +149,12 @@ public String getRouterIp() { public void setRouterIp(final String routerIp) { this.routerIp = routerIp; } + + public SslCertEntry[] getSslCerts() { + return sslCerts; + } + + public void setSslCerts(final SslCertEntry[] sslCerts) { + this.sslCerts = sslCerts; + } } diff --git a/core/src/main/java/com/cloud/agent/transport/ArrayTypeAdaptor.java b/core/src/main/java/com/cloud/agent/transport/ArrayTypeAdaptor.java index 7229f0903b74..eabcc82040d6 100644 --- a/core/src/main/java/com/cloud/agent/transport/ArrayTypeAdaptor.java +++ b/core/src/main/java/com/cloud/agent/transport/ArrayTypeAdaptor.java @@ -75,13 +75,17 @@ public T[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContex try { clazz = Class.forName(name); } catch (ClassNotFoundException e) { - throw new CloudRuntimeException("can't find " + name); + throw new JsonParseException("can't find " + name); } T cmd = (T)_gson.fromJson(entry.getValue(), clazz); cmds.add(cmd); } - Class type = ((Class)typeOfT).getComponentType(); - T[] ts = (T[])Array.newInstance(type, cmds.size()); - return cmds.toArray(ts); + try { + Class type = Class.forName(typeOfT.getTypeName().replace("[]", "")); + T[] ts = (T[])Array.newInstance(type, cmds.size()); + return cmds.toArray(ts); + } catch (ClassNotFoundException e) { + throw new CloudRuntimeException("can't find " + typeOfT.getTypeName()); + } } } diff --git a/core/src/main/java/com/cloud/network/HAProxyConfigurator.java b/core/src/main/java/com/cloud/network/HAProxyConfigurator.java index 9d07fc95c2fd..128652fc64fa 100644 --- a/core/src/main/java/com/cloud/network/HAProxyConfigurator.java +++ b/core/src/main/java/com/cloud/network/HAProxyConfigurator.java @@ -36,6 +36,8 @@ import com.cloud.agent.api.to.LoadBalancerTO.DestinationTO; import com.cloud.agent.api.to.LoadBalancerTO.StickinessPolicyTO; import com.cloud.agent.api.to.PortForwardingRuleTO; +import com.cloud.agent.resource.virtualnetwork.model.LoadBalancerRule.SslCertEntry; +import com.cloud.network.lb.LoadBalancingRule.LbSslCert; import com.cloud.network.rules.LbStickinessMethod.StickinessMethodType; import com.cloud.utils.Pair; import com.cloud.utils.net.NetUtils; @@ -52,6 +54,12 @@ public class HAProxyConfigurator implements LoadBalancerConfigurator { private static String[] defaultListen = {"listen vmops", "\tbind 0.0.0.0:9", "\toption transparent"}; + private static final String SSL_CERTS_DIR = "/etc/cloudstack/ssl/"; + + private static final String SSL_CONFIGURATION_INTERMEDIATE = " ssl-min-ver TLSv1.2 no-tls-tickets " + + "ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-GCM-SHA256 " + + "ciphersuites TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256"; + @Override public String[] generateConfiguration(final List fwRules) { // Group the rules by publicip:publicport @@ -469,30 +477,41 @@ private String getLbSubRuleForStickiness(final LoadBalancerTO lbTO) { return sb.toString(); } - private List getRulesForPool(final LoadBalancerTO lbTO, final boolean keepAliveEnabled) { + private List getRulesForPool(final LoadBalancerTO lbTO, final LoadBalancerConfigCommand lbCmd) { StringBuilder sb = new StringBuilder(); final String poolName = sb.append(lbTO.getSrcIp().replace(".", "_")).append('-').append(lbTO.getSrcPort()).toString(); final String publicIP = lbTO.getSrcIp(); final int publicPort = lbTO.getSrcPort(); final String algorithm = lbTO.getAlgorithm(); - final List result = new ArrayList(); - // add line like this: "listen 65_37_141_30-80\n\tbind 65.37.141.30:80" - sb = new StringBuilder(); - sb.append("listen ").append(poolName); - result.add(sb.toString()); + boolean sslOffloading = lbTO.getSslCert() != null && !lbTO.getSslCert().isRevoked() + && NetUtils.SSL_PROTO.equals(lbTO.getLbProtocol()); + + final List frontendConfigs = new ArrayList<>(); + final List backendConfigs = new ArrayList<>(); + final List result = new ArrayList<>(); + sb = new StringBuilder(); sb.append("\tbind ").append(publicIP).append(":").append(publicPort); - result.add(sb.toString()); + + if (sslOffloading) { + sb.append(" ssl crt ").append(SSL_CERTS_DIR).append(poolName).append(".pem"); + // check for http2 support + sb.append(" alpn h2,http/1.1"); + sb.append(SSL_CONFIGURATION_INTERMEDIATE); + sb.append("\n\thttp-request add-header X-Forwarded-Proto https"); + } + frontendConfigs.add(sb.toString()); + sb = new StringBuilder(); - sb.append("\t").append("balance ").append(algorithm); - result.add(sb.toString()); + sb.append("\t").append("balance ").append(algorithm.toLowerCase()); + backendConfigs.add(sb.toString()); int i = 0; - Boolean destsAvailable = false; + boolean destsAvailable = false; final String stickinessSubRule = getLbSubRuleForStickiness(lbTO); - final List dstSubRule = new ArrayList(); - final List dstWithCookieSubRule = new ArrayList(); + final List dstSubRule = new ArrayList<>(); + final List dstWithCookieSubRule = new ArrayList<>(); for (final DestinationTO dest : lbTO.getDestinations()) { // add line like this: "server 65_37_141_30-80_3 10.1.1.4:80 check" if (dest.isRevoked()) { @@ -500,15 +519,20 @@ private List getRulesForPool(final LoadBalancerTO lbTO, final boolean ke } sb = new StringBuilder(); sb.append("\t") - .append("server ") - .append(poolName) - .append("_") - .append(Integer.toString(i++)) - .append(" ") - .append(dest.getDestIp()) - .append(":") - .append(dest.getDestPort()) - .append(" check"); + .append("server ") + .append(poolName) + .append("_") + .append(i++) + .append(" ") + .append(dest.getDestIp()) + .append(":") + .append(dest.getDestPort()) + .append(" check"); + + if (sslOffloading) { + sb.append(SSL_CONFIGURATION_INTERMEDIATE); + } + if(lbTO.getLbProtocol() != null && lbTO.getLbProtocol().equals("tcp-proxy")) { sb.append(" send-proxy"); } @@ -520,9 +544,9 @@ private List getRulesForPool(final LoadBalancerTO lbTO, final boolean ke destsAvailable = true; } - Boolean httpbasedStickiness = false; + boolean httpbasedStickiness = false; /* attach stickiness sub rule only if the destinations are available */ - if (stickinessSubRule != null && destsAvailable == true) { + if (stickinessSubRule != null && destsAvailable) { for (final StickinessPolicyTO stickinessPolicy : lbTO.getStickinessPolicies()) { if (stickinessPolicy == null) { continue; @@ -530,35 +554,40 @@ private List getRulesForPool(final LoadBalancerTO lbTO, final boolean ke if (StickinessMethodType.LBCookieBased.getName().equalsIgnoreCase(stickinessPolicy.getMethodName()) || StickinessMethodType.AppCookieBased.getName().equalsIgnoreCase(stickinessPolicy.getMethodName())) { httpbasedStickiness = true; + break; } } if (httpbasedStickiness) { - result.addAll(dstWithCookieSubRule); + backendConfigs.addAll(dstWithCookieSubRule); } else { - result.addAll(dstSubRule); + backendConfigs.addAll(dstSubRule); } - result.add(stickinessSubRule); + backendConfigs.add(stickinessSubRule); } else { - result.addAll(dstSubRule); + backendConfigs.addAll(dstSubRule); } if (stickinessSubRule != null && !destsAvailable) { logger.warn("Haproxy stickiness policy for lb rule: " + lbTO.getSrcIp() + ":" + lbTO.getSrcPort() + ": Not Applied, cause: backends are unavailable"); } - if (publicPort == NetUtils.HTTP_PORT && !keepAliveEnabled || httpbasedStickiness) { - sb = new StringBuilder(); - sb.append("\t").append("mode http"); - result.add(sb.toString()); - sb = new StringBuilder(); - sb.append("\t").append("option httpclose"); - result.add(sb.toString()); + boolean keepAliveEnabled = lbCmd.keepAliveEnabled; + boolean http = (publicPort == NetUtils.HTTP_PORT && !keepAliveEnabled); + if (http || httpbasedStickiness || sslOffloading) { + frontendConfigs.add("\tmode http"); + String keepAliveLine = keepAliveEnabled ? "\tno option forceclose" : "\toption httpclose"; + frontendConfigs.add(keepAliveLine); } + // add line like this: "listen 65_37_141_30-80\n\tbind 65.37.141.30:80" + result.add(String.format("listen %s", poolName)); + result.addAll(frontendConfigs); + String cidrList = lbTO.getCidrList(); if (StringUtils.isNotBlank(cidrList)) { result.add(String.format("\tacl network_allowed src %s \n\ttcp-request connection reject if !network_allowed", cidrList)); } + result.addAll(backendConfigs); result.add(blankLine); return result; } @@ -566,15 +595,18 @@ private List getRulesForPool(final LoadBalancerTO lbTO, final boolean ke private String generateStatsRule(final LoadBalancerConfigCommand lbCmd, final String ruleName, final String statsIp) { final StringBuilder rule = new StringBuilder("\nlisten ").append(ruleName).append("\n\tbind ").append(statsIp).append(":").append(lbCmd.lbStatsPort); // TODO DH: write test for this in both cases - if (!lbCmd.keepAliveEnabled) { - logger.info("Haproxy mode http enabled"); - rule.append("\n\tmode http\n\toption httpclose"); + rule.append("\n\tmode http"); + if (lbCmd.keepAliveEnabled) { + logger.info("Haproxy option http-keep-alive enabled"); + } else { + logger.info("Haproxy option httpclose enabled"); + rule.append("\n\toption httpclose"); } rule.append("\n\tstats enable\n\tstats uri ") - .append(lbCmd.lbStatsUri) - .append("\n\tstats realm Haproxy\\ Statistics\n\tstats auth ") - .append(lbCmd.lbStatsAuth); - rule.append("\n"); + .append(lbCmd.lbStatsUri) + .append("\n\tstats realm Haproxy\\ Statistics\n\tstats auth ") + .append(lbCmd.lbStatsAuth) + .append("\n"); final String result = rule.toString(); if (logger.isDebugEnabled()) { logger.debug("Haproxystats rule: " + result); @@ -597,9 +629,6 @@ 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); @@ -644,7 +673,7 @@ public String[] generateConfiguration(final LoadBalancerConfigCommand lbCmd) { if (lbTO.isRevoked()) { continue; } - final List poolRules = getRulesForPool(lbTO, lbCmd.keepAliveEnabled); + final List poolRules = getRulesForPool(lbTO, lbCmd); result.addAll(poolRules); has_listener = true; } @@ -696,4 +725,30 @@ public String[][] generateFwRules(final LoadBalancerConfigCommand lbCmd) { return result; } + + @Override + public SslCertEntry[] generateSslCertEntries(LoadBalancerConfigCommand lbCmd) { + final Set sslCertEntries = new HashSet<>(); + for (final LoadBalancerTO lbTO : lbCmd.getLoadBalancers()) { + if (lbTO.getSslCert() != null) { + addSslCertEntry(sslCertEntries, lbTO); + } + } + final SslCertEntry[] result = sslCertEntries.toArray(new SslCertEntry[sslCertEntries.size()]); + return result; + } + + private void addSslCertEntry(Set sslCertEntries, LoadBalancerTO lbTO) { + final LbSslCert cert = lbTO.getSslCert(); + if (cert.isRevoked()) { + return; + } + if (lbTO.getLbProtocol() == null || ! lbTO.getLbProtocol().equals(NetUtils.SSL_PROTO)) { + return; + } + StringBuilder sb = new StringBuilder(); + final String name = sb.append(lbTO.getSrcIp().replace(".", "_")).append('-').append(lbTO.getSrcPort()).toString(); + final SslCertEntry sslCertEntry = new SslCertEntry(name, cert.getCert(), cert.getKey(), cert.getChain(), cert.getPassword()); + sslCertEntries.add(sslCertEntry); + } } diff --git a/core/src/main/java/com/cloud/network/LoadBalancerConfigurator.java b/core/src/main/java/com/cloud/network/LoadBalancerConfigurator.java index 0e19b1e606e9..8814f60b0714 100644 --- a/core/src/main/java/com/cloud/network/LoadBalancerConfigurator.java +++ b/core/src/main/java/com/cloud/network/LoadBalancerConfigurator.java @@ -23,6 +23,7 @@ import com.cloud.agent.api.routing.LoadBalancerConfigCommand; import com.cloud.agent.api.to.PortForwardingRuleTO; +import com.cloud.agent.resource.virtualnetwork.model.LoadBalancerRule.SslCertEntry; public interface LoadBalancerConfigurator { public final static int ADD = 0; @@ -34,4 +35,6 @@ public interface LoadBalancerConfigurator { public String[] generateConfiguration(LoadBalancerConfigCommand lbCmd); public String[][] generateFwRules(LoadBalancerConfigCommand lbCmd); + + public SslCertEntry[] generateSslCertEntries(LoadBalancerConfigCommand lbCmd); } diff --git a/core/src/main/java/com/cloud/resource/CommandWrapper.java b/core/src/main/java/com/cloud/resource/CommandWrapper.java index a839234117be..72d1348dfe70 100644 --- a/core/src/main/java/com/cloud/resource/CommandWrapper.java +++ b/core/src/main/java/com/cloud/resource/CommandWrapper.java @@ -19,10 +19,13 @@ package com.cloud.resource; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import com.cloud.agent.api.Answer; import com.cloud.agent.api.Command; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.LogManager; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.Script; public abstract class CommandWrapper { protected Logger logger = LogManager.getLogger(getClass()); @@ -33,4 +36,26 @@ public abstract class CommandWrapper*?![]{}~".indexOf(c) != -1) { + sanitized.append('\\'); + } + sanitized.append(c); + } + return sanitized.toString(); + } + + public void removeDpdkPort(String portToRemove) { + logger.debug("Removing DPDK port: " + portToRemove); + int port; + try { + port = Integer.valueOf(portToRemove); + } catch (NumberFormatException nfe) { + throw new CloudRuntimeException(String.format("Invalid DPDK port specified: '%s'", portToRemove)); + } + Script.executeCommand("ovs-vsctl", "del-port", String.valueOf(port)); + } } diff --git a/core/src/main/java/com/cloud/resource/ServerResource.java b/core/src/main/java/com/cloud/resource/ServerResource.java index 1602a78d9a47..23d200942a27 100644 --- a/core/src/main/java/com/cloud/resource/ServerResource.java +++ b/core/src/main/java/com/cloud/resource/ServerResource.java @@ -22,6 +22,7 @@ import com.cloud.agent.IAgentControl; import com.cloud.agent.api.Answer; import com.cloud.agent.api.Command; +import com.cloud.agent.api.PingAnswer; import com.cloud.agent.api.PingCommand; import com.cloud.agent.api.StartupCommand; import com.cloud.host.Host; @@ -50,6 +51,10 @@ public interface ServerResource extends Manager { */ StartupCommand[] initialize(); + default StartupCommand[] initialize(boolean isTransferredConnection) { + return initialize(); + } + /** * @param id id of the server to put in the PingCommand * @return PingCommand @@ -78,4 +83,13 @@ public interface ServerResource extends Manager { void setAgentControl(IAgentControl agentControl); + default boolean isExitOnFailures() { + return true; + } + + default boolean isAppendAgentNameToLogs() { + return false; + } + + default void processPingAnswer(PingAnswer answer) {}; } diff --git a/core/src/main/java/com/cloud/serializer/GsonHelper.java b/core/src/main/java/com/cloud/serializer/GsonHelper.java index 8288b7796807..7de98c08b7e0 100644 --- a/core/src/main/java/com/cloud/serializer/GsonHelper.java +++ b/core/src/main/java/com/cloud/serializer/GsonHelper.java @@ -21,6 +21,8 @@ import java.util.List; +import com.cloud.hypervisor.Hypervisor; +import org.apache.cloudstack.transport.HypervisorTypeAdaptor; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; @@ -54,11 +56,13 @@ public class GsonHelper { GsonBuilder LOGGERBuilder = new GsonBuilder(); LOGGERBuilder.disableHtmlEscaping(); LOGGERBuilder.setExclusionStrategies(new LoggingExclusionStrategy(LOGGER)); + LOGGERBuilder.serializeSpecialFloatingPointValues(); + // maybe add LOGGERBuilder.serializeNulls(); as well? s_gogger = setDefaultGsonConfig(LOGGERBuilder); LOGGER.info("Default Builder inited."); } - static Gson setDefaultGsonConfig(GsonBuilder builder) { + public static Gson setDefaultGsonConfig(GsonBuilder builder) { builder.setVersion(1.5); InterfaceTypeAdaptor dsAdaptor = new InterfaceTypeAdaptor(); builder.registerTypeAdapter(DataStoreTO.class, dsAdaptor); @@ -73,6 +77,7 @@ static Gson setDefaultGsonConfig(GsonBuilder builder) { builder.registerTypeAdapter(new TypeToken>() { }.getType(), new NwGroupsCommandTypeAdaptor()); builder.registerTypeAdapter(Storage.StoragePoolType.class, new StoragePoolTypeAdaptor()); + builder.registerTypeAdapter(Hypervisor.HypervisorType.class, new HypervisorTypeAdaptor()); Gson gson = builder.create(); dsAdaptor.initGson(gson); dtAdaptor.initGson(gson); 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..dd8e2abcd643 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,7 @@ public interface StorageProcessor { Answer copyVolumeFromPrimaryToPrimary(CopyCommand cmd); - public Answer checkDataStoreStoragePolicyCompliance(CheckDataStoreStoragePolicyComplainceCommand cmd); + public Answer checkDataStoreStoragePolicyCompliance(CheckDataStoreStoragePolicyComplianceCommand cmd); public Answer syncVolumePath(SyncVolumePathCommand cmd); } 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 7d8225462cab..318c069b0b0b 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) { @@ -142,7 +142,7 @@ protected Answer execute(CreateObjectCommand cmd) { } return new CreateObjectAnswer("not supported type"); } catch (Exception e) { - logger.debug("Failed to create object: " + data.getObjectType() + ": " + e.toString()); + logger.error("Failed to create object [{}] due to [{}].", data.getObjectType(), e.getMessage(), e); return new CreateObjectAnswer(e.toString()); } } diff --git a/core/src/main/java/com/cloud/storage/template/HttpTemplateDownloader.java b/core/src/main/java/com/cloud/storage/template/HttpTemplateDownloader.java index 92865caeb57a..6fe001de72c0 100755 --- a/core/src/main/java/com/cloud/storage/template/HttpTemplateDownloader.java +++ b/core/src/main/java/com/cloud/storage/template/HttpTemplateDownloader.java @@ -27,7 +27,9 @@ import java.io.RandomAccessFile; import java.net.URI; import java.net.URISyntaxException; +import java.util.Arrays; import java.util.Date; +import java.util.List; import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.storage.command.DownloadCommand.ResourceType; @@ -78,6 +80,19 @@ public class HttpTemplateDownloader extends ManagedContextRunnable implements Te private long maxTemplateSizeInBytes; private ResourceType resourceType = ResourceType.TEMPLATE; private final HttpMethodRetryHandler myretryhandler; + private boolean followRedirects = false; + private boolean isChunkedTransfer; + + protected static final List CUSTOM_HEADERS_FOR_CHUNKED_TRANSFER_SIZE = Arrays.asList( + "x-goog-stored-content-length", + "x-goog-meta-size", + "x-amz-meta-size", + "x-amz-meta-content-length", + "x-object-meta-size", + "x-original-content-length", + "x-oss-meta-content-length", + "x-file-size"); + private static final long MIN_FORMAT_VERIFICATION_SIZE = 1024 * 1024; public HttpTemplateDownloader(StorageLayer storageLayer, String downloadUrl, String toDir, DownloadCompleteCallback callback, long maxTemplateSizeInBytes, String user, String password, Proxy proxy, ResourceType resourceType) { @@ -109,7 +124,7 @@ public HttpTemplateDownloader(StorageLayer storageLayer, String downloadUrl, Str private GetMethod createRequest(String downloadUrl) { GetMethod request = new GetMethod(downloadUrl); request.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, myretryhandler); - request.setFollowRedirects(true); + request.setFollowRedirects(followRedirects); return request; } @@ -136,7 +151,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()); } @@ -203,13 +218,11 @@ public long download(boolean resume, DownloadCompleteCallback callback) { RandomAccessFile out = new RandomAccessFile(file, "rw"); ) { out.seek(localFileSize); - - logger.info("Starting download from " + downloadUrl + " to " + toFile + " remoteSize=" + toHumanReadableSize(remoteSize) + " , max size=" + toHumanReadableSize(maxTemplateSizeInBytes)); - - if (copyBytes(file, in, out)) return 0; - + logger.info("Starting download from {} to {} remoteSize={} , max size={}",downloadUrl, toFile, + toHumanReadableSize(remoteSize), toHumanReadableSize(maxTemplateSizeInBytes)); + boolean eof = copyBytes(file, in, out); Date finish = new Date(); - checkDowloadCompletion(); + checkDownloadCompletion(eof); downloadTime += finish.getTime() - start.getTime(); } finally { /* in.close() and out.close() */ } return totalBytes; @@ -235,28 +248,32 @@ public long download(boolean resume, DownloadCompleteCallback callback) { } private boolean copyBytes(File file, InputStream in, RandomAccessFile out) throws IOException { - int bytes; - byte[] block = new byte[CHUNK_SIZE]; + byte[] buffer = new byte[CHUNK_SIZE]; long offset = 0; - boolean done = false; VerifyFormat verifyFormat = new VerifyFormat(file); status = Status.IN_PROGRESS; - while (!done && status != Status.ABORTED && offset <= remoteSize) { - if ((bytes = in.read(block, 0, CHUNK_SIZE)) > -1) { - offset = writeBlock(bytes, out, block, offset); - if (!ResourceType.SNAPSHOT.equals(resourceType) && - !verifyFormat.isVerifiedFormat() && - (offset >= 1048576 || offset >= remoteSize)) { //let's check format after we get 1MB or full file - verifyFormat.invoke(); - } - } else { - done = true; + while (status != Status.ABORTED) { + int bytesRead = in.read(buffer, 0, CHUNK_SIZE); + if (bytesRead == -1) { + logger.debug("Reached EOF on input stream"); + break; + } + offset = writeBlock(bytesRead, out, buffer, offset); + if (!ResourceType.SNAPSHOT.equals(resourceType) + && !verifyFormat.isVerifiedFormat() + && (offset >= MIN_FORMAT_VERIFICATION_SIZE || offset >= remoteSize)) { + verifyFormat.invoke(); + } + if (offset >= remoteSize) { + logger.debug("Reached expected remote size limit: {} bytes", remoteSize); + break; } } out.getFD().sync(); - return false; + return !Status.ABORTED.equals(status); } + private long writeBlock(int bytes, RandomAccessFile out, byte[] block, long offset) throws IOException { out.write(block, 0, bytes); offset += bytes; @@ -265,11 +282,13 @@ private long writeBlock(int bytes, RandomAccessFile out, byte[] block, long offs return offset; } - private void checkDowloadCompletion() { + private void checkDownloadCompletion(boolean eof) { String downloaded = "(incomplete download)"; - if (totalBytes >= remoteSize) { + if (eof && ((totalBytes >= remoteSize) || (isChunkedTransfer && remoteSize == maxTemplateSizeInBytes))) { status = Status.DOWNLOAD_FINISHED; - downloaded = "(download complete remote=" + toHumanReadableSize(remoteSize) + " bytes)"; + downloaded = "(download complete remote=" + + (remoteSize == maxTemplateSizeInBytes ? toHumanReadableSize(remoteSize) : "unknown") + + " bytes)"; } errorString = "Downloaded " + toHumanReadableSize(totalBytes) + " bytes " + downloaded; } @@ -291,18 +310,42 @@ private void checkAndSetDownloadSize() { } } + protected long getRemoteSizeForChunkedTransfer() { + for (String headerKey : CUSTOM_HEADERS_FOR_CHUNKED_TRANSFER_SIZE) { + Header header = request.getResponseHeader(headerKey); + if (header == null) { + continue; + } + try { + return Long.parseLong(header.getValue()); + } catch (NumberFormatException ignored) {} + } + Header contentRangeHeader = request.getResponseHeader("Content-Range"); + if (contentRangeHeader != null) { + String contentRange = contentRangeHeader.getValue(); + if (contentRange != null && contentRange.contains("/")) { + String totalSize = contentRange.substring(contentRange.indexOf('/') + 1).trim(); + return Long.parseLong(totalSize); + } + } + return 0; + } + private boolean tryAndGetRemoteSize() { Header contentLengthHeader = request.getResponseHeader("content-length"); - boolean chunked = false; + isChunkedTransfer = false; long reportedRemoteSize = 0; if (contentLengthHeader == null) { Header chunkedHeader = request.getResponseHeader("Transfer-Encoding"); - if (chunkedHeader == null || !"chunked".equalsIgnoreCase(chunkedHeader.getValue())) { + if (chunkedHeader != null && "chunked".equalsIgnoreCase(chunkedHeader.getValue())) { + isChunkedTransfer = true; + reportedRemoteSize = getRemoteSizeForChunkedTransfer(); + logger.debug("{} is using chunked transfer encoding, possible remote size: {}", downloadUrl, + reportedRemoteSize); + } else { status = Status.UNRECOVERABLE_ERROR; errorString = " Failed to receive length of download "; return false; - } else if ("chunked".equalsIgnoreCase(chunkedHeader.getValue())) { - chunked = true; } } else { reportedRemoteSize = Long.parseLong(contentLengthHeader.getValue()); @@ -314,9 +357,11 @@ private boolean tryAndGetRemoteSize() { return false; } } - if (remoteSize == 0) { remoteSize = reportedRemoteSize; + if (remoteSize != 0) { + logger.debug("Remote size for {} found to be {}", downloadUrl, toHumanReadableSize(remoteSize)); + } } return true; } @@ -335,6 +380,12 @@ private boolean checkServerResponse(long localFileSize) throws IOException { } else if ((responseCode = client.executeMethod(request)) != HttpStatus.SC_OK) { status = Status.UNRECOVERABLE_ERROR; errorString = " HTTP Server returned " + responseCode + " (expected 200 OK) "; + if (List.of(HttpStatus.SC_MOVED_PERMANENTLY, HttpStatus.SC_MOVED_TEMPORARILY).contains(responseCode) + && !followRedirects) { + errorString = String.format("Failed to download %s due to redirection, response code: %d", + downloadUrl, responseCode); + logger.error(errorString); + } return true; //FIXME: retry? } return false; @@ -527,7 +578,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"); @@ -536,4 +587,12 @@ public VerifyFormat invoke() { return this; } } + + @Override + public void setFollowRedirects(boolean followRedirects) { + this.followRedirects = followRedirects; + if (this.request != null) { + this.request.setFollowRedirects(followRedirects); + } + } } 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 bf4be5825a1d..95ed0d1e76d0 100644 --- a/core/src/main/java/com/cloud/storage/template/MetalinkTemplateDownloader.java +++ b/core/src/main/java/com/cloud/storage/template/MetalinkTemplateDownloader.java @@ -58,7 +58,7 @@ public MetalinkTemplateDownloader(StorageLayer storageLayer, String downloadUrl, protected GetMethod createRequest(String downloadUrl) { GetMethod request = new GetMethod(downloadUrl); request.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, myretryhandler); - request.setFollowRedirects(true); + request.setFollowRedirects(followRedirects); if (!toFileSet) { String[] parts = downloadUrl.split("/"); String filename = parts[parts.length - 1]; @@ -108,7 +108,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; @@ -171,4 +171,12 @@ public Status getStatus() { public void setStatus(Status status) { this.status = status; } + + @Override + public void setFollowRedirects(boolean followRedirects) { + super.setFollowRedirects(followRedirects); + if (this.request != null) { + this.request.setFollowRedirects(followRedirects); + } + } } 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 34ba3c6b1eae..70df906d1ce3 100644 --- a/core/src/main/java/com/cloud/storage/template/S3TemplateDownloader.java +++ b/core/src/main/java/com/cloud/storage/template/S3TemplateDownloader.java @@ -34,6 +34,7 @@ import org.apache.cloudstack.storage.command.DownloadCommand.ResourceType; import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.URIException; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.params.HttpMethodParams; @@ -43,6 +44,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.Date; +import java.util.List; import static com.cloud.utils.NumbersUtil.toHumanReadableSize; import static java.util.Arrays.asList; @@ -70,8 +72,8 @@ public class S3TemplateDownloader extends ManagedContextRunnable implements Temp private long downloadTime; private long totalBytes; private long maxTemplateSizeInByte; - private boolean resume = false; + private boolean followRedirects = false; public S3TemplateDownloader(S3TO s3TO, String downloadUrl, String installPath, DownloadCompleteCallback downloadCompleteCallback, long maxTemplateSizeInBytes, String username, String password, Proxy proxy, ResourceType resourceType) { @@ -89,7 +91,7 @@ public S3TemplateDownloader(S3TO s3TO, String downloadUrl, String installPath, D this.getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, HTTPUtils.getHttpMethodRetryHandler(5)); // Follow redirects - this.getMethod.setFollowRedirects(true); + this.getMethod.setFollowRedirects(followRedirects); // Set file extension. this.fileExtension = StringUtils.substringAfterLast(StringUtils.substringAfterLast(downloadUrl, "/"), "."); @@ -122,10 +124,11 @@ public long download(boolean resume, DownloadCompleteCallback callback) { return 0; } - if (!HTTPUtils.verifyResponseCode(responseCode)) { + boolean failedDueToRedirection = List.of(HttpStatus.SC_MOVED_PERMANENTLY, + HttpStatus.SC_MOVED_TEMPORARILY).contains(responseCode) && !followRedirects; + if (!HTTPUtils.verifyResponseCode(responseCode) || failedDueToRedirection) { errorString = "Response code for GetMethod of " + downloadUrl + " is incorrect, responseCode: " + responseCode; logger.warn(errorString); - status = Status.UNRECOVERABLE_ERROR; return 0; } @@ -147,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; @@ -159,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; @@ -371,4 +374,12 @@ public long getTotalBytes() { public String getFileExtension() { return fileExtension; } + + @Override + public void setFollowRedirects(boolean followRedirects) { + this.followRedirects = followRedirects; + if (this.getMethod != null) { + this.getMethod.setFollowRedirects(followRedirects); + } + } } 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 db4dccb1302d..8719947cb4f0 100644 --- a/core/src/main/java/com/cloud/storage/template/SimpleHttpMultiFileDownloader.java +++ b/core/src/main/java/com/cloud/storage/template/SimpleHttpMultiFileDownloader.java @@ -25,6 +25,7 @@ import java.io.RandomAccessFile; import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.apache.cloudstack.managed.context.ManagedContextRunnable; @@ -71,6 +72,7 @@ public class SimpleHttpMultiFileDownloader extends ManagedContextRunnable implem private final HttpMethodRetryHandler retryHandler; private HashMap urlFileMap; + private boolean followRedirects = false; public SimpleHttpMultiFileDownloader(StorageLayer storageLayer, String[] downloadUrls, String toDir, DownloadCompleteCallback callback, long maxTemplateSizeInBytes, @@ -92,7 +94,7 @@ public SimpleHttpMultiFileDownloader(StorageLayer storageLayer, String[] downloa private GetMethod createRequest(String downloadUrl) { GetMethod request = new GetMethod(downloadUrl); request.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, retryHandler); - request.setFollowRedirects(true); + request.setFollowRedirects(followRedirects); return request; } @@ -168,7 +170,7 @@ private long downloadFile(String downloadUrl) { urlFileMap.put(downloadUrl, currentToFile); file = new File(currentToFile); long localFileSize = checkLocalFileSizeForResume(resume, file); - if (checkServerResponse(localFileSize)) return 0; + if (checkServerResponse(localFileSize, downloadUrl)) return 0; if (!tryAndGetRemoteSize()) return 0; if (!canHandleDownloadSize()) return 0; checkAndSetDownloadSize(); @@ -315,7 +317,7 @@ private boolean tryAndGetRemoteSize() { return true; } - private boolean checkServerResponse(long localFileSize) throws IOException { + private boolean checkServerResponse(long localFileSize, String downloadUrl) throws IOException { int responseCode = 0; if (localFileSize > 0) { @@ -329,6 +331,12 @@ private boolean checkServerResponse(long localFileSize) throws IOException { } else if ((responseCode = client.executeMethod(request)) != HttpStatus.SC_OK) { currentStatus = Status.UNRECOVERABLE_ERROR; errorString = " HTTP Server returned " + responseCode + " (expected 200 OK) "; + if (List.of(HttpStatus.SC_MOVED_PERMANENTLY, HttpStatus.SC_MOVED_TEMPORARILY).contains(responseCode) + && !followRedirects) { + errorString = String.format("Failed to download %s due to redirection, response code: %d", + downloadUrl, responseCode); + logger.error(errorString); + } return true; //FIXME: retry? } return false; @@ -476,4 +484,12 @@ public DownloadCommand.ResourceType getResourceType() { public Map getDownloadedFilesMap() { return urlFileMap; } + + @Override + public void setFollowRedirects(boolean followRedirects) { + this.followRedirects = followRedirects; + if (this.request != null) { + this.request.setFollowRedirects(followRedirects); + } + } } diff --git a/core/src/main/java/com/cloud/storage/template/TemplateConstants.java b/core/src/main/java/com/cloud/storage/template/TemplateConstants.java index d6622bed73ef..349997da1b7a 100644 --- a/core/src/main/java/com/cloud/storage/template/TemplateConstants.java +++ b/core/src/main/java/com/cloud/storage/template/TemplateConstants.java @@ -24,7 +24,7 @@ public final class TemplateConstants { public static final String DEFAULT_SNAPSHOT_ROOT_DIR = "snapshots"; public static final String DEFAULT_VOLUME_ROOT_DIR = "volumes"; public static final String DEFAULT_TMPLT_FIRST_LEVEL_DIR = "tmpl/"; - + public static final String DEFAULT_CHECKPOINT_ROOT_DIR = "checkpoints"; public static final String DEFAULT_SYSTEM_VM_TEMPLATE_PATH = "template/tmpl/1/"; public static final int DEFAULT_TMPLT_COPY_PORT = 80; diff --git a/core/src/main/java/com/cloud/storage/template/TemplateDownloader.java b/core/src/main/java/com/cloud/storage/template/TemplateDownloader.java index 5db3d2425a52..9fb1ca424424 100644 --- a/core/src/main/java/com/cloud/storage/template/TemplateDownloader.java +++ b/core/src/main/java/com/cloud/storage/template/TemplateDownloader.java @@ -92,4 +92,6 @@ enum Status { boolean isInited(); long getMaxTemplateSizeInBytes(); + + void setFollowRedirects(boolean followRedirects); } diff --git a/core/src/main/java/com/cloud/storage/template/TemplateDownloaderBase.java b/core/src/main/java/com/cloud/storage/template/TemplateDownloaderBase.java index cf6f4d27ecd7..f1cb21a1815a 100644 --- a/core/src/main/java/com/cloud/storage/template/TemplateDownloaderBase.java +++ b/core/src/main/java/com/cloud/storage/template/TemplateDownloaderBase.java @@ -41,6 +41,7 @@ public abstract class TemplateDownloaderBase extends ManagedContextRunnable impl protected long _start; protected StorageLayer _storage; protected boolean _inited = false; + protected boolean followRedirects = false; private long maxTemplateSizeInBytes; public TemplateDownloaderBase(StorageLayer storage, String downloadUrl, String toDir, long maxTemplateSizeInBytes, DownloadCompleteCallback callback) { @@ -147,4 +148,9 @@ public void setResume(boolean resume) { public boolean isInited() { return _inited; } + + @Override + public void setFollowRedirects(boolean followRedirects) { + this.followRedirects = followRedirects; + } } 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/agent/directdownload/CheckUrlCommand.java b/core/src/main/java/org/apache/cloudstack/agent/directdownload/CheckUrlCommand.java index b1b76da82112..325f61427a9f 100644 --- a/core/src/main/java/org/apache/cloudstack/agent/directdownload/CheckUrlCommand.java +++ b/core/src/main/java/org/apache/cloudstack/agent/directdownload/CheckUrlCommand.java @@ -28,6 +28,7 @@ public class CheckUrlCommand extends Command { private Integer connectTimeout; private Integer connectionRequestTimeout; private Integer socketTimeout; + private boolean followRedirects; public String getFormat() { return format; @@ -43,19 +44,25 @@ public String getUrl() { public Integer getSocketTimeout() { return socketTimeout; } - public CheckUrlCommand(final String format,final String url) { + public boolean isFollowRedirects() { + return followRedirects; + } + + public CheckUrlCommand(final String format, final String url, final boolean followRedirects) { super(); this.format = format; this.url = url; + this.followRedirects = followRedirects; } - public CheckUrlCommand(final String format,final String url, Integer connectTimeout, Integer connectionRequestTimeout, Integer socketTimeout) { + public CheckUrlCommand(final String format,final String url, Integer connectTimeout, Integer connectionRequestTimeout, Integer socketTimeout, final boolean followRedirects) { super(); this.format = format; this.url = url; this.connectTimeout = connectTimeout; this.socketTimeout = socketTimeout; this.connectionRequestTimeout = connectionRequestTimeout; + this.followRedirects = followRedirects; } @Override diff --git a/core/src/main/java/org/apache/cloudstack/agent/directdownload/DirectDownloadCommand.java b/core/src/main/java/org/apache/cloudstack/agent/directdownload/DirectDownloadCommand.java index 7e1ff0b34c40..b6748dca4342 100644 --- a/core/src/main/java/org/apache/cloudstack/agent/directdownload/DirectDownloadCommand.java +++ b/core/src/main/java/org/apache/cloudstack/agent/directdownload/DirectDownloadCommand.java @@ -45,7 +45,11 @@ public enum DownloadProtocol { private Long templateSize; private Storage.ImageFormat format; - protected DirectDownloadCommand (final String url, final Long templateId, final PrimaryDataStoreTO destPool, final String checksum, final Map headers, final Integer connectTimeout, final Integer soTimeout, final Integer connectionRequestTimeout) { + private boolean followRedirects; + + protected DirectDownloadCommand (final String url, final Long templateId, final PrimaryDataStoreTO destPool, + final String checksum, final Map headers, final Integer connectTimeout, + final Integer soTimeout, final Integer connectionRequestTimeout, final boolean followRedirects) { this.url = url; this.templateId = templateId; this.destData = destData; @@ -55,6 +59,7 @@ protected DirectDownloadCommand (final String url, final Long templateId, final this.connectTimeout = connectTimeout; this.soTimeout = soTimeout; this.connectionRequestTimeout = connectionRequestTimeout; + this.followRedirects = followRedirects; } public String getUrl() { @@ -137,4 +142,12 @@ public boolean executeInSequence() { public int getWaitInMillSeconds() { return getWait() * 1000; } + + public boolean isFollowRedirects() { + return followRedirects; + } + + public void setFollowRedirects(boolean followRedirects) { + this.followRedirects = followRedirects; + } } diff --git a/core/src/main/java/org/apache/cloudstack/agent/directdownload/HttpDirectDownloadCommand.java b/core/src/main/java/org/apache/cloudstack/agent/directdownload/HttpDirectDownloadCommand.java index f131b3b0a7fc..bdc00b3bc47c 100644 --- a/core/src/main/java/org/apache/cloudstack/agent/directdownload/HttpDirectDownloadCommand.java +++ b/core/src/main/java/org/apache/cloudstack/agent/directdownload/HttpDirectDownloadCommand.java @@ -24,8 +24,10 @@ public class HttpDirectDownloadCommand extends DirectDownloadCommand { - public HttpDirectDownloadCommand(String url, Long templateId, PrimaryDataStoreTO destPool, String checksum, Map headers, int connectTimeout, int soTimeout) { - super(url, templateId, destPool, checksum, headers, connectTimeout, soTimeout, null); + public HttpDirectDownloadCommand(String url, Long templateId, PrimaryDataStoreTO destPool, String checksum, + Map headers, int connectTimeout, int soTimeout, boolean followRedirects) { + super(url, templateId, destPool, checksum, headers, connectTimeout, soTimeout, + null, followRedirects); } } diff --git a/core/src/main/java/org/apache/cloudstack/agent/directdownload/HttpsDirectDownloadCommand.java b/core/src/main/java/org/apache/cloudstack/agent/directdownload/HttpsDirectDownloadCommand.java index dd88ad2a1551..a7e16eac91a5 100644 --- a/core/src/main/java/org/apache/cloudstack/agent/directdownload/HttpsDirectDownloadCommand.java +++ b/core/src/main/java/org/apache/cloudstack/agent/directdownload/HttpsDirectDownloadCommand.java @@ -25,7 +25,10 @@ public class HttpsDirectDownloadCommand extends DirectDownloadCommand { - public HttpsDirectDownloadCommand(String url, Long templateId, PrimaryDataStoreTO destPool, String checksum, Map headers, int connectTimeout, int soTimeout, int connectionRequestTimeout) { - super(url, templateId, destPool, checksum, headers, connectTimeout, soTimeout, connectionRequestTimeout); + public HttpsDirectDownloadCommand(String url, Long templateId, PrimaryDataStoreTO destPool, String checksum, + Map headers, int connectTimeout, int soTimeout, int connectionRequestTimeout, + boolean followRedirects) { + super(url, templateId, destPool, checksum, headers, connectTimeout, soTimeout, + connectionRequestTimeout, followRedirects); } } diff --git a/core/src/main/java/org/apache/cloudstack/agent/directdownload/MetalinkDirectDownloadCommand.java b/core/src/main/java/org/apache/cloudstack/agent/directdownload/MetalinkDirectDownloadCommand.java index a3edcebe7ded..7742994c76b1 100644 --- a/core/src/main/java/org/apache/cloudstack/agent/directdownload/MetalinkDirectDownloadCommand.java +++ b/core/src/main/java/org/apache/cloudstack/agent/directdownload/MetalinkDirectDownloadCommand.java @@ -24,8 +24,9 @@ public class MetalinkDirectDownloadCommand extends DirectDownloadCommand { - public MetalinkDirectDownloadCommand(String url, Long templateId, PrimaryDataStoreTO destPool, String checksum, Map headers, int connectTimeout, int soTimeout) { - super(url, templateId, destPool, checksum, headers, connectTimeout, soTimeout, null); + public MetalinkDirectDownloadCommand(String url, Long templateId, PrimaryDataStoreTO destPool, String checksum, + Map headers, int connectTimeout, int soTimeout, boolean followRedirects) { + super(url, templateId, destPool, checksum, headers, connectTimeout, soTimeout, null, followRedirects); } } diff --git a/core/src/main/java/org/apache/cloudstack/agent/directdownload/NfsDirectDownloadCommand.java b/core/src/main/java/org/apache/cloudstack/agent/directdownload/NfsDirectDownloadCommand.java index 0bf9c4d934b9..0e51d786230c 100644 --- a/core/src/main/java/org/apache/cloudstack/agent/directdownload/NfsDirectDownloadCommand.java +++ b/core/src/main/java/org/apache/cloudstack/agent/directdownload/NfsDirectDownloadCommand.java @@ -24,8 +24,9 @@ public class NfsDirectDownloadCommand extends DirectDownloadCommand { - public NfsDirectDownloadCommand(final String url, final Long templateId, final PrimaryDataStoreTO destPool, final String checksum, final Map headers) { - super(url, templateId, destPool, checksum, headers, null, null, null); + public NfsDirectDownloadCommand(final String url, final Long templateId, final PrimaryDataStoreTO destPool, + final String checksum, final Map headers) { + super(url, templateId, destPool, checksum, headers, null, null, null, false); } } diff --git a/core/src/main/java/org/apache/cloudstack/agent/lb/SetupMSListCommand.java b/core/src/main/java/org/apache/cloudstack/agent/lb/SetupMSListCommand.java index 50cf956c9e75..864a3e22eb3e 100644 --- a/core/src/main/java/org/apache/cloudstack/agent/lb/SetupMSListCommand.java +++ b/core/src/main/java/org/apache/cloudstack/agent/lb/SetupMSListCommand.java @@ -26,20 +26,28 @@ public class SetupMSListCommand extends Command { private List msList; + private List avoidMsList; private String lbAlgorithm; private Long lbCheckInterval; + private Boolean triggerHostLb; - public SetupMSListCommand(final List msList, final String lbAlgorithm, final Long lbCheckInterval) { + public SetupMSListCommand(final List msList, final List avoidMsList, final String lbAlgorithm, final Long lbCheckInterval, final Boolean triggerHostLb) { super(); this.msList = msList; + this.avoidMsList = avoidMsList; this.lbAlgorithm = lbAlgorithm; this.lbCheckInterval = lbCheckInterval; + this.triggerHostLb = triggerHostLb; } public List getMsList() { return msList; } + public List getAvoidMsList() { + return avoidMsList; + } + public String getLbAlgorithm() { return lbAlgorithm; } @@ -48,9 +56,12 @@ public Long getLbCheckInterval() { return lbCheckInterval; } + public boolean getTriggerHostLb() { + return triggerHostLb; + } + @Override public boolean executeInSequence() { return false; } - } diff --git a/core/src/main/java/org/apache/cloudstack/agent/routing/ManageServiceCommand.java b/core/src/main/java/org/apache/cloudstack/agent/routing/ManageServiceCommand.java new file mode 100644 index 000000000000..c83a5b695740 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/agent/routing/ManageServiceCommand.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.agent.routing; + +import com.cloud.agent.api.routing.NetworkElementCommand; + +public class ManageServiceCommand extends NetworkElementCommand { + + String serviceName; + String action; + + @Override + public boolean executeInSequence() { + return true; + } + + protected ManageServiceCommand() { + } + + public ManageServiceCommand(String serviceName, String action) { + this.serviceName = serviceName; + this.action = action; + } + + public String getServiceName() { + return serviceName; + } + + public String getAction() { + return action; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/BackupAnswer.java b/core/src/main/java/org/apache/cloudstack/backup/BackupAnswer.java new file mode 100644 index 000000000000..ffc67b628a7e --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/BackupAnswer.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.backup; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; + +import java.util.Map; + +public class BackupAnswer extends Answer { + private Long size; + private Long virtualSize; + private Map volumes; + Boolean needsCleanup; + + public BackupAnswer(final Command command, final boolean success, final String details) { + super(command, success, details); + } + + public Long getSize() { + return size; + } + + public void setSize(Long size) { + this.size = size; + } + + public Long getVirtualSize() { + return virtualSize; + } + + public void setVirtualSize(Long virtualSize) { + this.virtualSize = virtualSize; + } + + public Map getVolumes() { + return volumes; + } + + public void setVolumes(Map volumes) { + this.volumes = volumes; + } + + public Boolean getNeedsCleanup() { + if (needsCleanup == null) { + return false; + } + return needsCleanup; + } + + public void setNeedsCleanup(Boolean needsCleanup) { + this.needsCleanup = needsCleanup; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/BackupStorageStatsAnswer.java b/core/src/main/java/org/apache/cloudstack/backup/BackupStorageStatsAnswer.java new file mode 100644 index 000000000000..eabf6877ba6e --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/BackupStorageStatsAnswer.java @@ -0,0 +1,50 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.backup; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; + +public class BackupStorageStatsAnswer extends Answer { + private Long totalSize; + private Long usedSize; + + public BackupStorageStatsAnswer(final Command command, final boolean success, final String details) { + super(command, success, details); + this.totalSize = 0L; + this.usedSize = 0L; + } + + public Long getTotalSize() { + return totalSize; + } + + public void setTotalSize(Long totalSize) { + this.totalSize = totalSize; + } + + public Long getUsedSize() { + return usedSize; + } + + public void setUsedSize(Long usedSize) { + this.usedSize = usedSize; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/DeleteBackupCommand.java b/core/src/main/java/org/apache/cloudstack/backup/DeleteBackupCommand.java new file mode 100644 index 000000000000..16c611af998e --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/DeleteBackupCommand.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.backup; + +import com.cloud.agent.api.Command; +import com.cloud.agent.api.LogLevel; + +public class DeleteBackupCommand extends Command { + private String backupPath; + private String backupRepoType; + private String backupRepoAddress; + @LogLevel(LogLevel.Log4jLevel.Off) + private String mountOptions; + + public DeleteBackupCommand(String backupPath, String backupRepoType, String backupRepoAddress, String mountOptions) { + super(); + this.backupPath = backupPath; + this.backupRepoType = backupRepoType; + this.backupRepoAddress = backupRepoAddress; + this.mountOptions = mountOptions; + } + + public String getBackupPath() { + return backupPath; + } + + public void setBackupPath(String backupPath) { + this.backupPath = backupPath; + } + + public String getBackupRepoType() { + return backupRepoType; + } + + public void setBackupRepoType(String backupRepoType) { + this.backupRepoType = backupRepoType; + } + + public String getBackupRepoAddress() { + return backupRepoAddress; + } + + public void setBackupRepoAddress(String backupRepoAddress) { + this.backupRepoAddress = backupRepoAddress; + } + + public String getMountOptions() { + return mountOptions == null ? "" : mountOptions; + } + + public void setMountOptions(String mountOptions) { + this.mountOptions = mountOptions; + } + + @Override + public boolean executeInSequence() { + return true; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/GetBackupStorageStatsCommand.java b/core/src/main/java/org/apache/cloudstack/backup/GetBackupStorageStatsCommand.java new file mode 100644 index 000000000000..1ceeac17e52e --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/GetBackupStorageStatsCommand.java @@ -0,0 +1,66 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.backup; + +import com.cloud.agent.api.Command; +import com.cloud.agent.api.LogLevel; + +public class GetBackupStorageStatsCommand extends Command { + private String backupRepoType; + private String backupRepoAddress; + @LogLevel(LogLevel.Log4jLevel.Off) + private String mountOptions; + + public GetBackupStorageStatsCommand(String backupRepoType, String backupRepoAddress, String mountOptions) { + super(); + this.backupRepoType = backupRepoType; + this.backupRepoAddress = backupRepoAddress; + this.mountOptions = mountOptions; + } + + public String getBackupRepoType() { + return backupRepoType; + } + + public void setBackupRepoType(String backupRepoType) { + this.backupRepoType = backupRepoType; + } + + public String getBackupRepoAddress() { + return backupRepoAddress; + } + + public void setBackupRepoAddress(String backupRepoAddress) { + this.backupRepoAddress = backupRepoAddress; + } + + public String getMountOptions() { + return mountOptions == null ? "" : mountOptions; + } + + public void setMountOptions(String mountOptions) { + this.mountOptions = mountOptions; + } + + @Override + public boolean executeInSequence() { + return true; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/RestoreBackupCommand.java b/core/src/main/java/org/apache/cloudstack/backup/RestoreBackupCommand.java new file mode 100644 index 000000000000..f5ad5fbea2c7 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/RestoreBackupCommand.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.backup; + +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; + +public class RestoreBackupCommand extends Command { + private String vmName; + private String backupPath; + private String backupRepoType; + private String backupRepoAddress; + private List backupVolumesUUIDs; + private List restoreVolumePools; + private List restoreVolumePaths; + private List backupFiles; + private String diskType; + private Boolean vmExists; + private VirtualMachine.State vmState; + private Integer mountTimeout; + + protected RestoreBackupCommand() { + super(); + } + + public String getVmName() { + return vmName; + } + + public void setVmName(String vmName) { + this.vmName = vmName; + } + + public String getBackupPath() { + return backupPath; + } + + public void setBackupPath(String backupPath) { + this.backupPath = backupPath; + } + + public String getBackupRepoType() { + return backupRepoType; + } + + public void setBackupRepoType(String backupRepoType) { + this.backupRepoType = backupRepoType; + } + + public String getBackupRepoAddress() { + return backupRepoAddress; + } + + public void setBackupRepoAddress(String backupRepoAddress) { + this.backupRepoAddress = backupRepoAddress; + } + + public List getRestoreVolumePools() { + return restoreVolumePools; + } + + public void setRestoreVolumePools(List restoreVolumePools) { + this.restoreVolumePools = restoreVolumePools; + } + + public List getRestoreVolumePaths() { + return restoreVolumePaths; + } + + public void setRestoreVolumePaths(List restoreVolumePaths) { + this.restoreVolumePaths = restoreVolumePaths; + } + + public List getBackupFiles() { + return backupFiles; + } + + public void setBackupFiles(List backupFiles) { + this.backupFiles = backupFiles; + } + + public Boolean isVmExists() { + return vmExists; + } + + public void setVmExists(Boolean vmExists) { + this.vmExists = vmExists; + } + + public String getDiskType() { + return diskType; + } + + public void setDiskType(String diskType) { + this.diskType = diskType; + } + + public String getMountOptions() { + return mountOptions; + } + + public void setMountOptions(String mountOptions) { + this.mountOptions = mountOptions; + } + + public VirtualMachine.State getVmState() { + return vmState; + } + + public void setVmState(VirtualMachine.State vmState) { + this.vmState = vmState; + } + + @LogLevel(LogLevel.Log4jLevel.Off) + private String mountOptions; + @Override + + public boolean executeInSequence() { + return true; + } + + public List getBackupVolumesUUIDs() { + return backupVolumesUUIDs; + } + + 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/TakeBackupCommand.java b/core/src/main/java/org/apache/cloudstack/backup/TakeBackupCommand.java new file mode 100644 index 000000000000..5402b6b24760 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/TakeBackupCommand.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.backup; + +import com.cloud.agent.api.Command; +import com.cloud.agent.api.LogLevel; +import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; + +import java.util.List; + +public class TakeBackupCommand extends Command { + private String vmName; + 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; + + public TakeBackupCommand(String vmName, String backupPath) { + super(); + this.vmName = vmName; + this.backupPath = backupPath; + } + + public String getVmName() { + return vmName; + } + + public void setVmName(String vmName) { + this.vmName = vmName; + } + + public String getBackupPath() { + return backupPath; + } + + public void setBackupPath(String backupPath) { + this.backupPath = backupPath; + } + + public String getBackupRepoType() { + return backupRepoType; + } + + public void setBackupRepoType(String backupRepoType) { + this.backupRepoType = backupRepoType; + } + + public String getBackupRepoAddress() { + return backupRepoAddress; + } + + public void setBackupRepoAddress(String backupRepoAddress) { + this.backupRepoAddress = backupRepoAddress; + } + + public String getMountOptions() { + return mountOptions; + } + + 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; + } + + public void setVolumePaths(List volumePaths) { + this.volumePaths = volumePaths; + } + + public Boolean getQuiesce() { + return quiesce; + } + + public void setQuiesce(Boolean quiesce) { + this.quiesce = quiesce; + } + + @Override + public boolean executeInSequence() { + return true; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/CommandInfo.java b/core/src/main/java/org/apache/cloudstack/command/CommandInfo.java new file mode 100644 index 000000000000..b9bb702e3459 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/CommandInfo.java @@ -0,0 +1,124 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.command; + +import com.cloud.agent.api.Command; +import com.cloud.serializer.GsonHelper; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import java.util.Date; + +public class CommandInfo { + public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss.SSSZ"; + public static final Gson GSON = GsonHelper.setDefaultGsonConfig(new GsonBuilder().setDateFormat(DATE_FORMAT)); + + long requestSeq; + Command.State state; + Date startTime; + Date updateTime; + String commandName; + String command; + int timeout; + String answerName; + String answer; + + public CommandInfo() { + } + + public CommandInfo(long requestSeq, Command command, Command.State state) { + this.requestSeq = requestSeq; + this.state = state; + this.startTime = this.updateTime = new Date(); + this.commandName = command.getClass().getName(); + this.command = GSON.toJson(command); + this.timeout = command.getWait(); + } + + public long getRequestSeq() { + return requestSeq; + } + + public void setRequestSeq(long requestSeq) { + this.requestSeq = requestSeq; + } + + public Command.State getState() { + return state; + } + + public void setState(Command.State state) { + this.state = state; + this.updateTime = new Date(); + } + + public Date getStartTime() { + return startTime; + } + + public void setStartTime(Date startTime) { + this.startTime = startTime; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + public String getCommandName() { + return commandName; + } + + public void setCommandName(String commandName) { + this.commandName = commandName; + } + + public String getCommand() { + return command; + } + + public void setCommand(String command) { + this.command = command; + } + + public int getTimeout() { + return timeout; + } + + public void setTimeout(int timeout) { + this.timeout = timeout; + } + + public String getAnswerName() { + return answerName; + } + + public void setAnswerName(String answerName) { + this.answerName = answerName; + } + + public String getAnswer() { + return answer; + } + + public void setAnswer(String answer) { + this.answer = answer; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileAnswer.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileAnswer.java new file mode 100644 index 000000000000..e8d27e1fa712 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileAnswer.java @@ -0,0 +1,45 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.command; + +import com.cloud.agent.api.Answer; +import org.apache.cloudstack.api.ApiCommandResourceType; + +public class ReconcileAnswer extends Answer { + + ApiCommandResourceType resourceType; + Long resourceId; + + public ApiCommandResourceType getResourceType() { + return resourceType; + } + + public void setResourceType(ApiCommandResourceType resourceType) { + this.resourceType = resourceType; + } + + public Long getResourceId() { + return resourceId; + } + + public void setResourceId(Long resourceId) { + this.resourceId = resourceId; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileCommand.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileCommand.java new file mode 100644 index 000000000000..262aefb30ac6 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileCommand.java @@ -0,0 +1,33 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.command; + +import com.cloud.agent.api.Command; + +public class ReconcileCommand extends Command { + + @Override + public boolean executeInSequence() { + return false; + } + + @Override + public int getWait() { + return 30; // timeout is 30 seconds + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileCommandUtils.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileCommandUtils.java new file mode 100644 index 000000000000..8acc02a730f1 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileCommandUtils.java @@ -0,0 +1,192 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.command; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.JsonSyntaxException; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.text.SimpleDateFormat; + +public class ReconcileCommandUtils { + + protected static final Logger LOGGER = LogManager.getLogger(ReconcileCommandUtils.class.getName()); + + public static void createLogFileForCommand(final String logPath, final Command cmd) { + updateLogFileForCommand(logPath, cmd, Command.State.CREATED); + } + + public static void updateLogFileForCommand(final String logPath, final Command cmd, final Command.State state) { + if (cmd.isReconcile()) { + String logFileName = getLogFileNameForCommand(logPath, cmd); + LOGGER.debug(String.format("Updating log file %s with %s state", logFileName, state)); + File logFile = new File(logFileName); + CommandInfo commandInfo = null; + if (logFile.exists()) { + commandInfo = readLogFileForCommand(logFileName); + logFile.delete(); + } + if (commandInfo == null) { + commandInfo = new CommandInfo(cmd.getRequestSequence(), cmd, state); + } else { + commandInfo.setState(state); + } + try { + BufferedWriter writer = new BufferedWriter(new FileWriter(logFile)); + writer.write(CommandInfo.GSON.toJson(commandInfo)); + writer.close(); + } catch (IOException e) { + LOGGER.error(String.format("Failed to write log file %s", logFile)); + } + } + } + + public static void updateLogFileForCommand(final String logFullPath, final Command.State state) { + File logFile = new File(logFullPath); + LOGGER.debug(String.format("Updating log file %s with %s state", logFile.getName(), state)); + if (!logFile.exists()) { + return; + } + CommandInfo commandInfo = readLogFileForCommand(logFullPath); + if (commandInfo != null) { + commandInfo.setState(state); + } + logFile.delete(); + try { + BufferedWriter writer = new BufferedWriter(new FileWriter(logFile)); + writer.write(CommandInfo.GSON.toJson(commandInfo)); + writer.close(); + } catch (IOException e) { + LOGGER.error(String.format("Failed to write log file %s", logFile)); + } + } + + public static void deleteLogFileForCommand(final String logPath, final Command cmd) { + if (cmd.isReconcile()) { + File logFile = new File(getLogFileNameForCommand(logPath, cmd)); + LOGGER.debug(String.format("Removing log file %s", logFile.getName())); + if (logFile.exists()) { + logFile.delete(); + } + } + } + + public static void deleteLogFile(final String logFullPath) { + File logFile = new File(logFullPath); + LOGGER.debug(String.format("Removing log file %s ", logFile.getName())); + if (logFile.exists()) { + logFile.delete(); + } + } + + public static String getLogFileNameForCommand(final String logPath, final Command cmd) { + return String.format("%s/%s-%s.json", logPath, cmd.getRequestSequence(), cmd); + } + + public static CommandInfo readLogFileForCommand(final String logFullPath) { + try { + ObjectMapper objectMapper = new ObjectMapper(); + SimpleDateFormat df = new SimpleDateFormat(CommandInfo.DATE_FORMAT); + objectMapper.setDateFormat(df); + return objectMapper.readValue(new File(logFullPath), CommandInfo.class); + } catch (IOException e) { + LOGGER.error(String.format("Failed to read log file %s: %s", logFullPath, e.getMessage())); + return null; + } + } + + public static Command parseCommandInfo(final CommandInfo commandInfo) { + if (commandInfo.getCommandName() == null || commandInfo.getCommand() == null) { + return null; + } + return parseCommandInfo(commandInfo.getCommandName(), commandInfo.getCommand()); + } + + public static Command parseCommandInfo(final String commandName, final String commandInfo) { + Object parsedObject = null; + try { + Class commandClazz = Class.forName(commandName); + parsedObject = CommandInfo.GSON.fromJson(commandInfo, commandClazz); + } catch (ClassNotFoundException | JsonSyntaxException e) { + LOGGER.error(String.format("Failed to parse command from CommandInfo %s due to %s", commandInfo, e.getMessage())); + } + if (parsedObject != null) { + return (Command) parsedObject; + } + return null; + } + + public static Answer parseAnswerFromCommandInfo(final CommandInfo commandInfo) { + if (commandInfo.getAnswerName() == null || commandInfo.getAnswer() == null) { + return null; + } + return parseAnswerFromAnswerInfo(commandInfo.getAnswerName(), commandInfo.getAnswer()); + } + + public static Answer parseAnswerFromAnswerInfo(final String answerName, final String answerInfo) { + Object parsedObject = null; + try { + Class commandClazz = Class.forName(answerName); + parsedObject = CommandInfo.GSON.fromJson(answerInfo, commandClazz); + } catch (ClassNotFoundException | JsonSyntaxException e) { + LOGGER.error(String.format("Failed to parse answer from answerInfo %s due to %s", answerInfo, e.getMessage())); + } + if (parsedObject != null) { + return (Answer) parsedObject; + } + return null; + } + + public static void updateLogFileWithAnswerForCommand(final String logPath, final Command cmd, final Answer answer) { + if (cmd.isReconcile()) { + String logFileName = getLogFileNameForCommand(logPath, cmd); + LOGGER.debug(String.format("Updating log file %s with answer %s", logFileName, answer)); + File logFile = new File(logFileName); + if (!logFile.exists()) { + return; + } + CommandInfo commandInfo = readLogFileForCommand(logFile.getAbsolutePath()); + if (commandInfo == null) { + return; + } + if (Command.State.STARTED.equals(commandInfo.getState())) { + if (answer.getResult()) { + commandInfo.setState(Command.State.COMPLETED); + } else { + commandInfo.setState(Command.State.FAILED); + } + } + commandInfo.setAnswerName(answer.toString()); + commandInfo.setAnswer(CommandInfo.GSON.toJson(answer)); + try { + BufferedWriter writer = new BufferedWriter(new FileWriter(logFile)); + writer.write(CommandInfo.GSON.toJson(commandInfo)); + writer.close(); + } catch (IOException e) { + LOGGER.error(String.format("Failed to write log file %s", logFile)); + } + } + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileCopyAnswer.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileCopyAnswer.java new file mode 100644 index 000000000000..82a24fa7fa5e --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileCopyAnswer.java @@ -0,0 +1,56 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.command; + +import org.apache.cloudstack.storage.volume.VolumeOnStorageTO; + +public class ReconcileCopyAnswer extends ReconcileVolumeAnswer { + + boolean isSkipped = false; + String reason; + + public ReconcileCopyAnswer(boolean isSkipped, String reason) { + super(); + this.isSkipped = isSkipped; + this.reason = reason; + } + + public ReconcileCopyAnswer(boolean isSkipped, boolean result, String reason) { + super(); + this.isSkipped = isSkipped; + this.result = result; + this.reason = reason; + } + + public ReconcileCopyAnswer(VolumeOnStorageTO volumeOnSource, VolumeOnStorageTO volumeOnDestination) { + this.isSkipped = false; + this.result = true; + this.volumeOnSource = volumeOnSource; + this.volumeOnDestination = volumeOnDestination; + } + + public boolean isSkipped() { + return isSkipped; + } + + public String getReason() { + return reason; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileCopyCommand.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileCopyCommand.java new file mode 100644 index 000000000000..36a678833d0f --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileCopyCommand.java @@ -0,0 +1,53 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.command; + +import com.cloud.agent.api.to.DataTO; + +import java.util.Map; + +public class ReconcileCopyCommand extends ReconcileCommand { + + DataTO srcData; + DataTO destData; + Map option; // details of source volume + Map option2; // details of destination volume + + public ReconcileCopyCommand(DataTO srcData, DataTO destData, Map option, Map option2) { + this.srcData = srcData; + this.destData = destData; + this.option = option; + this.option2 = option2; + } + + public DataTO getSrcData() { + return srcData; + } + + public DataTO getDestData() { + return destData; + } + + public Map getOption() { + return option; + } + + public Map getOption2() { + return option2; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateAnswer.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateAnswer.java new file mode 100644 index 000000000000..6313267c7e4c --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateAnswer.java @@ -0,0 +1,68 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.command; + +import com.cloud.vm.VirtualMachine; + +import java.util.List; + +public class ReconcileMigrateAnswer extends ReconcileAnswer { + + Long hostId; + String vmName; + VirtualMachine.State vmState; + List vmDisks; + + public ReconcileMigrateAnswer() { + } + + public ReconcileMigrateAnswer(String vmName, VirtualMachine.State vmState) { + this.vmName = vmName; + this.vmState = vmState; + } + + public Long getHostId() { + return hostId; + } + + public void setHostId(Long hostId) { + this.hostId = hostId; + } + + public String getVmName() { + return vmName; + } + + public VirtualMachine.State getVmState() { + return vmState; + } + + public void setVmState(VirtualMachine.State vmState) { + this.vmState = vmState; + } + + public List getVmDisks() { + return vmDisks; + } + + public void setVmDisks(List vmDisks) { + this.vmDisks = vmDisks; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateCommand.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateCommand.java new file mode 100644 index 000000000000..50e1c68a65f6 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateCommand.java @@ -0,0 +1,31 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.command; + +public class ReconcileMigrateCommand extends ReconcileCommand { + + String vmName; + + public ReconcileMigrateCommand(String vmName) { + this.vmName = vmName; + } + + public String getVmName() { + return vmName; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateVolumeAnswer.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateVolumeAnswer.java new file mode 100644 index 000000000000..ebbd913f9718 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateVolumeAnswer.java @@ -0,0 +1,50 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.command; + +import org.apache.cloudstack.storage.volume.VolumeOnStorageTO; + +import java.util.List; + +public class ReconcileMigrateVolumeAnswer extends ReconcileVolumeAnswer { + + String vmName; + List vmDiskPaths; + + public ReconcileMigrateVolumeAnswer(VolumeOnStorageTO volumeOnSource, VolumeOnStorageTO volumeOnDestination) { + super(volumeOnSource, volumeOnDestination); + } + + public String getVmName() { + return vmName; + } + + public void setVmName(String vmName) { + this.vmName = vmName; + } + + public List getVmDiskPaths() { + return vmDiskPaths; + } + + public void setVmDiskPaths(List vmDiskPaths) { + this.vmDiskPaths = vmDiskPaths; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateVolumeCommand.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateVolumeCommand.java new file mode 100644 index 000000000000..e3e752494068 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateVolumeCommand.java @@ -0,0 +1,48 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.command; + +import com.cloud.agent.api.to.DataTO; + +public class ReconcileMigrateVolumeCommand extends ReconcileCommand { + + DataTO srcData; + DataTO destData; + String vmName; + + public ReconcileMigrateVolumeCommand(DataTO srcData, DataTO destData) { + this.srcData = srcData; + this.destData = destData; + } + + public DataTO getSrcData() { + return srcData; + } + + public DataTO getDestData() { + return destData; + } + + public String getVmName() { + return vmName; + } + + public void setVmName(String vmName) { + this.vmName = vmName; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileVolumeAnswer.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileVolumeAnswer.java new file mode 100644 index 000000000000..da10bf23aeeb --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileVolumeAnswer.java @@ -0,0 +1,46 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.command; + +import org.apache.cloudstack.storage.volume.VolumeOnStorageTO; + +public class ReconcileVolumeAnswer extends ReconcileAnswer { + + // (1) null object: volume is not available. For example the source is secondary storage + // (2) otherwise, if volumeOnSource.getPath() is null, the volume cannot be found on primary storage pool + VolumeOnStorageTO volumeOnSource; + VolumeOnStorageTO volumeOnDestination; + + public ReconcileVolumeAnswer() { + } + + public ReconcileVolumeAnswer(VolumeOnStorageTO volumeOnSource, VolumeOnStorageTO volumeOnDestination) { + this.volumeOnSource = volumeOnSource; + this.volumeOnDestination = volumeOnDestination; + } + + public VolumeOnStorageTO getVolumeOnSource() { + return volumeOnSource; + } + + public VolumeOnStorageTO getVolumeOnDestination() { + return volumeOnDestination; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadHelper.java b/core/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadHelper.java index be8841f3c885..0e0e2f0b57b8 100644 --- a/core/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadHelper.java +++ b/core/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadHelper.java @@ -35,27 +35,30 @@ public class DirectDownloadHelper { * Get direct template downloader from direct download command and destination pool */ public static DirectTemplateDownloader getDirectTemplateDownloaderFromCommand(DirectDownloadCommand cmd, - String destPoolLocalPath, - String temporaryDownloadPath) { + String destPoolLocalPath, String temporaryDownloadPath) { if (cmd instanceof HttpDirectDownloadCommand) { - return new HttpDirectTemplateDownloader(cmd.getUrl(), cmd.getTemplateId(), destPoolLocalPath, cmd.getChecksum(), cmd.getHeaders(), - cmd.getConnectTimeout(), cmd.getSoTimeout(), temporaryDownloadPath); + return new HttpDirectTemplateDownloader(cmd.getUrl(), cmd.getTemplateId(), destPoolLocalPath, + cmd.getChecksum(), cmd.getHeaders(), cmd.getConnectTimeout(), cmd.getSoTimeout(), + temporaryDownloadPath, cmd.isFollowRedirects()); } else if (cmd instanceof HttpsDirectDownloadCommand) { - return new HttpsDirectTemplateDownloader(cmd.getUrl(), cmd.getTemplateId(), destPoolLocalPath, cmd.getChecksum(), cmd.getHeaders(), - cmd.getConnectTimeout(), cmd.getSoTimeout(), cmd.getConnectionRequestTimeout(), temporaryDownloadPath); + return new HttpsDirectTemplateDownloader(cmd.getUrl(), cmd.getTemplateId(), destPoolLocalPath, + cmd.getChecksum(), cmd.getHeaders(), cmd.getConnectTimeout(), cmd.getSoTimeout(), + cmd.getConnectionRequestTimeout(), temporaryDownloadPath, cmd.isFollowRedirects()); } else if (cmd instanceof NfsDirectDownloadCommand) { - return new NfsDirectTemplateDownloader(cmd.getUrl(), destPoolLocalPath, cmd.getTemplateId(), cmd.getChecksum(), temporaryDownloadPath); + return new NfsDirectTemplateDownloader(cmd.getUrl(), destPoolLocalPath, cmd.getTemplateId(), + cmd.getChecksum(), temporaryDownloadPath); } else if (cmd instanceof MetalinkDirectDownloadCommand) { - return new MetalinkDirectTemplateDownloader(cmd.getUrl(), destPoolLocalPath, cmd.getTemplateId(), cmd.getChecksum(), cmd.getHeaders(), - cmd.getConnectTimeout(), cmd.getSoTimeout(), temporaryDownloadPath); + return new MetalinkDirectTemplateDownloader(cmd.getUrl(), destPoolLocalPath, cmd.getTemplateId(), + cmd.getChecksum(), cmd.getHeaders(), cmd.getConnectTimeout(), cmd.getSoTimeout(), + temporaryDownloadPath, cmd.isFollowRedirects()); } else { throw new IllegalArgumentException("Unsupported protocol, please provide HTTP(S), NFS or a metalink"); } } - public static boolean checkUrlExistence(String url) { + public static boolean checkUrlExistence(String url, boolean followRedirects) { try { - DirectTemplateDownloader checker = getCheckerDownloader(url, null, null, null); + DirectTemplateDownloader checker = getCheckerDownloader(url, null, null, null, followRedirects); return checker.checkUrl(url); } catch (CloudRuntimeException e) { LOGGER.error(String.format("Cannot check URL %s is reachable due to: %s", url, e.getMessage()), e); @@ -63,9 +66,9 @@ public static boolean checkUrlExistence(String url) { } } - public static boolean checkUrlExistence(String url, Integer connectTimeout, Integer connectionRequestTimeout, Integer socketTimeout) { + public static boolean checkUrlExistence(String url, Integer connectTimeout, Integer connectionRequestTimeout, Integer socketTimeout, boolean followRedirects) { try { - DirectTemplateDownloader checker = getCheckerDownloader(url, connectTimeout, connectionRequestTimeout, socketTimeout); + DirectTemplateDownloader checker = getCheckerDownloader(url, connectTimeout, connectionRequestTimeout, socketTimeout, followRedirects); return checker.checkUrl(url); } catch (CloudRuntimeException e) { LOGGER.error(String.format("Cannot check URL %s is reachable due to: %s", url, e.getMessage()), e); @@ -73,27 +76,27 @@ public static boolean checkUrlExistence(String url, Integer connectTimeout, Inte } } - private static DirectTemplateDownloader getCheckerDownloader(String url, Integer connectTimeout, Integer connectionRequestTimeout, Integer socketTimeout) { + private static DirectTemplateDownloader getCheckerDownloader(String url, Integer connectTimeout, Integer connectionRequestTimeout, Integer socketTimeout, boolean followRedirects) { if (url.toLowerCase().startsWith("https:")) { - return new HttpsDirectTemplateDownloader(url, connectTimeout, connectionRequestTimeout, socketTimeout); + return new HttpsDirectTemplateDownloader(url, connectTimeout, connectionRequestTimeout, socketTimeout, followRedirects); } else if (url.toLowerCase().startsWith("http:")) { - return new HttpDirectTemplateDownloader(url, connectTimeout, socketTimeout); + return new HttpDirectTemplateDownloader(url, connectTimeout, socketTimeout, followRedirects); } else if (url.toLowerCase().startsWith("nfs:")) { return new NfsDirectTemplateDownloader(url); } else if (url.toLowerCase().endsWith(".metalink")) { - return new MetalinkDirectTemplateDownloader(url, connectTimeout, socketTimeout); + return new MetalinkDirectTemplateDownloader(url, connectTimeout, socketTimeout, followRedirects); } else { throw new CloudRuntimeException(String.format("Cannot find a download checker for url: %s", url)); } } - public static Long getFileSize(String url, String format) { - DirectTemplateDownloader checker = getCheckerDownloader(url, null, null, null); + public static Long getFileSize(String url, String format, boolean followRedirects) { + DirectTemplateDownloader checker = getCheckerDownloader(url, null, null, null, followRedirects); return checker.getRemoteFileSize(url, format); } - public static Long getFileSize(String url, String format, Integer connectTimeout, Integer connectionRequestTimeout, Integer socketTimeout) { - DirectTemplateDownloader checker = getCheckerDownloader(url, connectTimeout, connectionRequestTimeout, socketTimeout); + public static Long getFileSize(String url, String format, Integer connectTimeout, Integer connectionRequestTimeout, Integer socketTimeout, boolean followRedirects) { + DirectTemplateDownloader checker = getCheckerDownloader(url, connectTimeout, connectionRequestTimeout, socketTimeout, followRedirects); return checker.getRemoteFileSize(url, format); } } 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 b6a872aca7cd..a1485463eaa0 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 @@ -43,16 +43,19 @@ public abstract class DirectTemplateDownloaderImpl implements DirectTemplateDown private String checksum; private boolean redownload = false; protected String temporaryDownloadPath; + private boolean followRedirects; protected Logger logger = LogManager.getLogger(getClass()); protected DirectTemplateDownloaderImpl(final String url, final String destPoolPath, final Long templateId, - final String checksum, final String temporaryDownloadPath) { + final String checksum, final String temporaryDownloadPath, + final boolean followRedirects) { this.url = url; this.destPoolPath = destPoolPath; this.templateId = templateId; this.checksum = checksum; this.temporaryDownloadPath = temporaryDownloadPath; + this.followRedirects = followRedirects; } private static String directDownloadDir = "template"; @@ -112,6 +115,14 @@ public boolean isRedownload() { return redownload; } + public boolean isFollowRedirects() { + return followRedirects; + } + + public void setFollowRedirects(boolean followRedirects) { + this.followRedirects = followRedirects; + } + /** * Create download directory (if it does not exist) */ @@ -136,16 +147,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); @@ -162,7 +173,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 70cdb8ed467d..c4a802ecdbc6 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 @@ -48,13 +48,14 @@ public class HttpDirectTemplateDownloader extends DirectTemplateDownloaderImpl { protected GetMethod request; protected Map reqHeaders = new HashMap<>(); - protected HttpDirectTemplateDownloader(String url, Integer connectTimeout, Integer socketTimeout) { - this(url, null, null, null, null, connectTimeout, socketTimeout, null); + protected HttpDirectTemplateDownloader(String url, Integer connectTimeout, Integer socketTimeout, boolean followRedirects) { + this(url, null, null, null, null, connectTimeout, socketTimeout, null, followRedirects); } public HttpDirectTemplateDownloader(String url, Long templateId, String destPoolPath, String checksum, - Map headers, Integer connectTimeout, Integer soTimeout, String downloadPath) { - super(url, destPoolPath, templateId, checksum, downloadPath); + Map headers, Integer connectTimeout, Integer soTimeout, String downloadPath, + boolean followRedirects) { + super(url, destPoolPath, templateId, checksum, downloadPath, followRedirects); s_httpClientManager.getParams().setConnectionTimeout(connectTimeout == null ? 5000 : connectTimeout); s_httpClientManager.getParams().setSoTimeout(soTimeout == null ? 5000 : soTimeout); client = new HttpClient(s_httpClientManager); @@ -66,7 +67,7 @@ public HttpDirectTemplateDownloader(String url, Long templateId, String destPool protected GetMethod createRequest(String downloadUrl, Map headers) { GetMethod request = new GetMethod(downloadUrl); - request.setFollowRedirects(true); + request.setFollowRedirects(this.isFollowRedirects()); if (MapUtils.isNotEmpty(headers)) { for (String key : headers.keySet()) { request.setRequestHeader(key, headers.get(key)); @@ -81,7 +82,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(); @@ -93,14 +94,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()); @@ -109,9 +110,11 @@ protected Pair performDownload() { @Override public boolean checkUrl(String url) { HeadMethod httpHead = new HeadMethod(url); + httpHead.setFollowRedirects(this.isFollowRedirects()); try { - if (client.executeMethod(httpHead) != HttpStatus.SC_OK) { - logger.error(String.format("Invalid URL: %s", url)); + int responseCode = client.executeMethod(httpHead); + if (responseCode != HttpStatus.SC_OK) { + logger.error(String.format("HTTP HEAD request to URL: %s failed, response code: %d", url, responseCode)); return false; } return true; @@ -126,9 +129,9 @@ public boolean checkUrl(String url) { @Override public Long getRemoteFileSize(String url, String format) { if ("qcow2".equalsIgnoreCase(format)) { - return QCOW2Utils.getVirtualSize(url); + return QCOW2Utils.getVirtualSizeFromUrl(url, this.isFollowRedirects()); } else { - return UriUtils.getRemoteSize(url); + return UriUtils.getRemoteSize(url, this.isFollowRedirects()); } } 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 fbf5f7281116..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 @@ -39,9 +39,7 @@ import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import org.apache.cloudstack.utils.security.SSLUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.io.IOUtils; @@ -55,6 +53,7 @@ import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; +import org.apache.http.ssl.SSLContexts; import org.apache.http.util.EntityUtils; import com.cloud.utils.Pair; @@ -68,20 +67,26 @@ public class HttpsDirectTemplateDownloader extends DirectTemplateDownloaderImpl protected CloseableHttpClient httpsClient; private HttpUriRequest req; - protected HttpsDirectTemplateDownloader(String url, Integer connectTimeout, Integer connectionRequestTimeout, Integer socketTimeout) { - this(url, null, null, null, null, connectTimeout, socketTimeout, connectionRequestTimeout, null); + protected HttpsDirectTemplateDownloader(String url, Integer connectTimeout, Integer connectionRequestTimeout, Integer socketTimeout, boolean followRedirects) { + this(url, null, null, null, null, connectTimeout, socketTimeout, connectionRequestTimeout, null, followRedirects); } - public HttpsDirectTemplateDownloader(String url, Long templateId, String destPoolPath, String checksum, Map headers, - Integer connectTimeout, Integer soTimeout, Integer connectionRequestTimeout, String temporaryDownloadPath) { - super(url, destPoolPath, templateId, checksum, temporaryDownloadPath); + public HttpsDirectTemplateDownloader(String url, Long templateId, String destPoolPath, String checksum, + Map headers, Integer connectTimeout, Integer soTimeout, + Integer connectionRequestTimeout, String temporaryDownloadPath, boolean followRedirects) { + super(url, destPoolPath, templateId, checksum, temporaryDownloadPath, followRedirects); SSLContext sslcontext = getSSLContext(); SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslcontext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); RequestConfig config = RequestConfig.custom() .setConnectTimeout(connectTimeout == null ? 5000 : connectTimeout) .setConnectionRequestTimeout(connectionRequestTimeout == null ? 5000 : connectionRequestTimeout) - .setSocketTimeout(soTimeout == null ? 5000 : soTimeout).build(); - httpsClient = HttpClients.custom().setSSLSocketFactory(factory).setDefaultRequestConfig(config).build(); + .setSocketTimeout(soTimeout == null ? 5000 : soTimeout) + .setRedirectsEnabled(followRedirects) + .build(); + httpsClient = HttpClients.custom() + .setSSLSocketFactory(factory) + .setDefaultRequestConfig(config) + .build(); createUriRequest(url, headers); String downloadDir = getDirectDownloadTempPath(templateId); File tempFile = createTemporaryDirectoryAndFile(downloadDir); @@ -90,9 +95,10 @@ public HttpsDirectTemplateDownloader(String url, Long templateId, String destPoo protected void createUriRequest(String downloadUrl, Map headers) { req = new HttpGet(downloadUrl); + setFollowRedirects(this.isFollowRedirects()); if (MapUtils.isNotEmpty(headers)) { - for (String headerKey: headers.keySet()) { - req.setHeader(headerKey, headers.get(headerKey)); + for (Map.Entry entry : headers.entrySet()) { + req.setHeader(entry.getKey(), entry.getValue()); } } } @@ -113,10 +119,10 @@ private SSLContext getSSLContext() { String password = "changeit"; defaultKeystore.load(is, password.toCharArray()); } - TrustManager[] tm = HttpsMultiTrustManager.getTrustManagersFromKeyStores(customKeystore, defaultKeystore); - SSLContext sslContext = SSLUtils.getSSLContext(); - sslContext.init(null, tm, null); - return sslContext; + return SSLContexts.custom() + .loadTrustMaterial(customKeystore, null) + .loadTrustMaterial(defaultKeystore, null) + .build(); } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException | KeyManagementException e) { logger.error(String.format("Failure getting SSL context for HTTPS downloader, using default SSL context: %s", e.getMessage()), e); try { @@ -143,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"); } @@ -153,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()); @@ -164,8 +170,9 @@ public boolean checkUrl(String url) { HttpHead httpHead = new HttpHead(url); try { CloseableHttpResponse response = httpsClient.execute(httpHead); - if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { - logger.error(String.format("Invalid URL: %s", url)); + int responseCode = response.getStatusLine().getStatusCode(); + if (responseCode != HttpStatus.SC_OK) { + logger.error(String.format("HTTP HEAD request to URL: %s failed, response code: %d", url, responseCode)); return false; } return true; diff --git a/core/src/main/java/org/apache/cloudstack/direct/download/HttpsMultiTrustManager.java b/core/src/main/java/org/apache/cloudstack/direct/download/HttpsMultiTrustManager.java deleted file mode 100644 index fe47847c36ca..000000000000 --- a/core/src/main/java/org/apache/cloudstack/direct/download/HttpsMultiTrustManager.java +++ /dev/null @@ -1,102 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package org.apache.cloudstack.direct.download; - -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509TrustManager; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; - -public class HttpsMultiTrustManager implements X509TrustManager { - - private final List trustManagers; - - public HttpsMultiTrustManager(KeyStore... keystores) { - List trustManagers = new ArrayList<>(); - trustManagers.add(getTrustManager(null)); - for (KeyStore keystore : keystores) { - trustManagers.add(getTrustManager(keystore)); - } - this.trustManagers = ImmutableList.copyOf(trustManagers); - } - - public static TrustManager[] getTrustManagersFromKeyStores(KeyStore... keyStore) { - return new TrustManager[] { new HttpsMultiTrustManager(keyStore) }; - - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { - for (X509TrustManager trustManager : trustManagers) { - try { - trustManager.checkClientTrusted(chain, authType); - return; - } catch (CertificateException ignored) {} - } - throw new CertificateException("None of the TrustManagers trust this certificate chain"); - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { - for (X509TrustManager trustManager : trustManagers) { - try { - trustManager.checkServerTrusted(chain, authType); - return; - } catch (CertificateException ignored) {} - } - throw new CertificateException("None of the TrustManagers trust this certificate chain"); - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - ImmutableList.Builder certificates = ImmutableList.builder(); - for (X509TrustManager trustManager : trustManagers) { - for (X509Certificate cert : trustManager.getAcceptedIssuers()) { - certificates.add(cert); - } - } - return Iterables.toArray(certificates.build(), X509Certificate.class); - } - - public X509TrustManager getTrustManager(KeyStore keystore) { - return getTrustManager(TrustManagerFactory.getDefaultAlgorithm(), keystore); - } - - public X509TrustManager getTrustManager(String algorithm, KeyStore keystore) { - TrustManagerFactory factory; - try { - factory = TrustManagerFactory.getInstance(algorithm); - factory.init(keystore); - return Iterables.getFirst(Iterables.filter( - Arrays.asList(factory.getTrustManagers()), X509TrustManager.class), null); - } catch (NoSuchAlgorithmException | KeyStoreException e) { - e.printStackTrace(); - } - return null; - } -} diff --git a/core/src/main/java/org/apache/cloudstack/direct/download/MetalinkDirectTemplateDownloader.java b/core/src/main/java/org/apache/cloudstack/direct/download/MetalinkDirectTemplateDownloader.java index 24d642460c5f..2050b9ef09f7 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 @@ -39,16 +39,15 @@ public class MetalinkDirectTemplateDownloader extends DirectTemplateDownloaderIm private Integer soTimeout; protected DirectTemplateDownloader createDownloaderForMetalinks(String url, Long templateId, - String destPoolPath, String checksum, - Map headers, - Integer connectTimeout, Integer soTimeout, - Integer connectionRequestTimeout, String temporaryDownloadPath) { + String destPoolPath, String checksum, Map headers, Integer connectTimeout, + Integer soTimeout, Integer connectionRequestTimeout, String temporaryDownloadPath) { if (url.toLowerCase().startsWith("https:")) { return new HttpsDirectTemplateDownloader(url, templateId, destPoolPath, checksum, headers, - connectTimeout, soTimeout, connectionRequestTimeout, temporaryDownloadPath); + connectTimeout, soTimeout, connectionRequestTimeout, temporaryDownloadPath, + this.isFollowRedirects()); } else if (url.toLowerCase().startsWith("http:")) { return new HttpDirectTemplateDownloader(url, templateId, destPoolPath, checksum, headers, - connectTimeout, soTimeout, temporaryDownloadPath); + connectTimeout, soTimeout, temporaryDownloadPath, this.isFollowRedirects()); } else if (url.toLowerCase().startsWith("nfs:")) { return new NfsDirectTemplateDownloader(url); } else { @@ -57,13 +56,14 @@ protected DirectTemplateDownloader createDownloaderForMetalinks(String url, Long } } - protected MetalinkDirectTemplateDownloader(String url, Integer connectTimeout, Integer socketTimeout) { - this(url, null, null, null, null, connectTimeout, socketTimeout, null); + protected MetalinkDirectTemplateDownloader(String url, Integer connectTimeout, Integer socketTimeout, boolean followRedirects) { + this(url, null, null, null, null, connectTimeout, socketTimeout, null, followRedirects); } public MetalinkDirectTemplateDownloader(String url, String destPoolPath, Long templateId, String checksum, - Map headers, Integer connectTimeout, Integer soTimeout, String downloadPath) { - super(url, destPoolPath, templateId, checksum, downloadPath); + Map headers, Integer connectTimeout, Integer soTimeout, String downloadPath, + boolean followRedirects) { + super(url, destPoolPath, templateId, checksum, downloadPath, followRedirects); this.headers = headers; this.connectTimeout = connectTimeout; this.soTimeout = soTimeout; @@ -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,7 +93,7 @@ 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 { @@ -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 0b7866104b18..21184ef07fe9 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 @@ -57,8 +57,9 @@ protected NfsDirectTemplateDownloader(String url) { this(url, null, null, null, null); } - public NfsDirectTemplateDownloader(String url, String destPool, Long templateId, String checksum, String downloadPath) { - super(url, destPool, templateId, checksum, downloadPath); + public NfsDirectTemplateDownloader(String url, String destPool, Long templateId, String checksum, + String downloadPath) { + super(url, destPool, templateId, checksum, downloadPath, false); parseUrl(); } diff --git a/core/src/main/java/org/apache/cloudstack/storage/command/CheckDataStoreStoragePolicyComplainceCommand.java b/core/src/main/java/org/apache/cloudstack/storage/command/CheckDataStoreStoragePolicyComplainceCommand.java deleted file mode 100644 index f9544b873ef2..000000000000 --- a/core/src/main/java/org/apache/cloudstack/storage/command/CheckDataStoreStoragePolicyComplainceCommand.java +++ /dev/null @@ -1,61 +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.storage.command; - -import com.cloud.agent.api.to.StorageFilerTO; - -public class CheckDataStoreStoragePolicyComplainceCommand extends StorageSubSystemCommand { - - String storagePolicyId; - private StorageFilerTO storagePool; - - public CheckDataStoreStoragePolicyComplainceCommand(String storagePolicyId, StorageFilerTO storagePool) { - super(); - - this.storagePolicyId = storagePolicyId; - this.storagePool = storagePool; - } - - @Override - public void setExecuteInSequence(boolean inSeq) { - } - - @Override - public boolean executeInSequence() { - return false; - } - - - public String getStoragePolicyId() { - return storagePolicyId; - } - - public void setStoragePolicyId(String storagePolicyId) { - this.storagePolicyId = storagePolicyId; - } - - public StorageFilerTO getStoragePool() { - return storagePool; - } - - public void setStoragePool(StorageFilerTO storagePool) { - this.storagePool = storagePool; - } -} diff --git a/core/src/main/java/org/apache/cloudstack/storage/command/CheckDataStoreStoragePolicyComplianceCommand.java b/core/src/main/java/org/apache/cloudstack/storage/command/CheckDataStoreStoragePolicyComplianceCommand.java new file mode 100644 index 000000000000..4544e98451f3 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/storage/command/CheckDataStoreStoragePolicyComplianceCommand.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.storage.command; + +import com.cloud.agent.api.to.StorageFilerTO; + +public class CheckDataStoreStoragePolicyComplianceCommand extends StorageSubSystemCommand { + + String storagePolicyId; + private StorageFilerTO storagePool; + + public CheckDataStoreStoragePolicyComplianceCommand(String storagePolicyId, StorageFilerTO storagePool) { + super(); + + this.storagePolicyId = storagePolicyId; + this.storagePool = storagePool; + } + + @Override + public void setExecuteInSequence(boolean inSeq) { + } + + @Override + public boolean executeInSequence() { + return false; + } + + + public String getStoragePolicyId() { + return storagePolicyId; + } + + public void setStoragePolicyId(String storagePolicyId) { + this.storagePolicyId = storagePolicyId; + } + + public StorageFilerTO getStoragePool() { + return storagePool; + } + + public void setStoragePool(StorageFilerTO storagePool) { + this.storagePool = storagePool; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/storage/command/CopyCommand.java b/core/src/main/java/org/apache/cloudstack/storage/command/CopyCommand.java index aac082a01333..36550420909b 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/command/CopyCommand.java +++ b/core/src/main/java/org/apache/cloudstack/storage/command/CopyCommand.java @@ -93,4 +93,9 @@ public Map getOptions2() { public void setExecuteInSequence(final boolean inSeq) { executeInSequence = inSeq; } + + @Override + public boolean isReconcile() { + return true; + } } diff --git a/core/src/main/java/org/apache/cloudstack/storage/command/DownloadCommand.java b/core/src/main/java/org/apache/cloudstack/storage/command/DownloadCommand.java index 4032ac0b6322..f44220f31d03 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/command/DownloadCommand.java +++ b/core/src/main/java/org/apache/cloudstack/storage/command/DownloadCommand.java @@ -49,6 +49,8 @@ public static enum ResourceType { private DataStoreTO _store; private DataStoreTO cacheStore; + private boolean followRedirects = false; + protected DownloadCommand() { } @@ -65,6 +67,7 @@ public DownloadCommand(DownloadCommand that) { installPath = that.installPath; _store = that._store; _proxy = that._proxy; + followRedirects = that.followRedirects; } public DownloadCommand(TemplateObjectTO template, Long maxDownloadSizeInBytes) { @@ -80,6 +83,7 @@ public DownloadCommand(TemplateObjectTO template, Long maxDownloadSizeInBytes) { setSecUrl(((NfsTO)_store).getUrl()); } this.maxDownloadSizeInBytes = maxDownloadSizeInBytes; + this.followRedirects = template.isFollowRedirects(); } public DownloadCommand(TemplateObjectTO template, String user, String passwd, Long maxDownloadSizeInBytes) { @@ -95,6 +99,7 @@ public DownloadCommand(VolumeObjectTO volume, Long maxDownloadSizeInBytes, Strin _store = volume.getDataStore(); this.maxDownloadSizeInBytes = maxDownloadSizeInBytes; resourceType = ResourceType.VOLUME; + this.followRedirects = volume.isFollowRedirects(); } public DownloadCommand(SnapshotObjectTO snapshot, Long maxDownloadSizeInBytes, String url) { @@ -194,4 +199,12 @@ public void setCacheStore(DataStoreTO cacheStore) { public DataStoreTO getCacheStore() { return cacheStore; } + + public boolean isFollowRedirects() { + return followRedirects; + } + + public void setFollowRedirects(boolean followRedirects) { + this.followRedirects = followRedirects; + } } 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..253a2607a72c 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 @@ -57,8 +57,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 +70,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() { @@ -216,4 +216,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/to/DownloadableObjectTO.java b/core/src/main/java/org/apache/cloudstack/storage/to/DownloadableObjectTO.java new file mode 100644 index 000000000000..70db8fafffc9 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/storage/to/DownloadableObjectTO.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.storage.to; + +public class DownloadableObjectTO { + protected boolean followRedirects = false; + + public boolean isFollowRedirects() { + return followRedirects; + } + + public void setFollowRedirects(boolean followRedirects) { + this.followRedirects = followRedirects; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/storage/to/ImageStoreTO.java b/core/src/main/java/org/apache/cloudstack/storage/to/ImageStoreTO.java index 046a2ab9410c..4bf292056732 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/to/ImageStoreTO.java +++ b/core/src/main/java/org/apache/cloudstack/storage/to/ImageStoreTO.java @@ -23,6 +23,7 @@ import com.cloud.agent.api.to.DataStoreTO; import com.cloud.storage.DataStoreRole; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; public class ImageStoreTO implements DataStoreTO { private String type; @@ -78,15 +79,9 @@ public DataStoreRole getRole() { @Override public String toString() { - return new StringBuilder("ImageStoreTO[type=").append(type) - .append("|provider=") - .append(providerName) - .append("|role=") - .append(role) - .append("|uri=") - .append(uri) - .append("]") - .toString(); + return String.format("ImageStoreTO %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "uuid", "type", "providerName", "role", "uri")); } @Override diff --git a/core/src/main/java/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java b/core/src/main/java/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java index a6a74176c136..2c758fa50874 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java +++ b/core/src/main/java/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java @@ -26,6 +26,7 @@ import com.cloud.agent.api.to.DataStoreTO; import com.cloud.storage.DataStoreRole; import com.cloud.storage.Storage.StoragePoolType; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; public class PrimaryDataStoreTO implements DataStoreTO { public static final String MANAGED = PrimaryDataStore.MANAGED; @@ -145,15 +146,9 @@ public String getPathSeparator() { @Override public String toString() { - return new StringBuilder("PrimaryDataStoreTO[uuid=").append(uuid) - .append("|name=") - .append(name) - .append("|id=") - .append(id) - .append("|pooltype=") - .append(poolType) - .append("]") - .toString(); + return String.format("PrimaryDataStoreTO %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "poolType")); } public Boolean isFullCloneFlag() { 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 70cb6d155b04..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 @@ -30,23 +30,30 @@ import com.cloud.agent.api.to.DataTO; import com.cloud.hypervisor.Hypervisor.HypervisorType; -public class SnapshotObjectTO implements DataTO { +public class SnapshotObjectTO extends DownloadableObjectTO implements DataTO { private String path; private VolumeObjectTO volume; private String parentSnapshotPath; private DataStoreTO dataStore; + private DataStoreTO imageStore; + private boolean kvmIncrementalSnapshot = false; private String vmName; private String name; private HypervisorType hypervisorType; private long id; private boolean quiescevm; private String[] parents; + private DataStoreTO parentStore; + private String checkpointPath; private Long physicalSize = (long) 0; private long accountId; - public SnapshotObjectTO() { + } + @Override + public DataObjectType getObjectType() { + return DataObjectType.SNAPSHOT; } public SnapshotObjectTO(SnapshotInfo snapshot) { @@ -59,27 +66,28 @@ public SnapshotObjectTO(SnapshotInfo snapshot) { this.setVmName(vol.getAttachedVmName()); } - SnapshotInfo parentSnapshot = snapshot.getParent(); - ArrayList parentsArry = new ArrayList(); - if (parentSnapshot != null) { - this.parentSnapshotPath = parentSnapshot.getPath(); - while(parentSnapshot != null) { - parentsArry.add(parentSnapshot.getPath()); - parentSnapshot = parentSnapshot.getParent(); - } - parents = parentsArry.toArray(new String[parentsArry.size()]); - ArrayUtils.reverse(parents); - } - this.dataStore = snapshot.getDataStore().getTO(); this.setName(snapshot.getName()); this.hypervisorType = snapshot.getHypervisorType(); this.quiescevm = false; - } - @Override - public DataObjectType getObjectType() { - return DataObjectType.SNAPSHOT; + this.checkpointPath = snapshot.getCheckpointPath(); + this.kvmIncrementalSnapshot = snapshot.isKvmIncrementalSnapshot(); + + SnapshotInfo parentSnapshot = snapshot.getParent(); + + if (parentSnapshot == null || (HypervisorType.KVM.equals(snapshot.getHypervisorType()) && !parentSnapshot.isKvmIncrementalSnapshot())) { + return; + } + + ArrayList parentsArray = new ArrayList<>(); + this.parentSnapshotPath = parentSnapshot.getPath(); + while (parentSnapshot != null) { + parentsArray.add(parentSnapshot.getPath()); + parentSnapshot = parentSnapshot.getParent(); + } + parents = parentsArray.toArray(new String[parentsArray.size()]); + ArrayUtils.reverse(parents); } @Override @@ -91,6 +99,30 @@ public void setDataStore(DataStoreTO store) { this.dataStore = store; } + public DataStoreTO getImageStore() { + return imageStore; + } + + public void setImageStore(DataStoreTO imageStore) { + this.imageStore = imageStore; + } + + public boolean isKvmIncrementalSnapshot() { + return kvmIncrementalSnapshot; + } + + public void setKvmIncrementalSnapshot(boolean kvmIncrementalSnapshot) { + this.kvmIncrementalSnapshot = kvmIncrementalSnapshot; + } + + public String getCheckpointPath() { + return checkpointPath; + } + + public void setCheckpointPath(String checkpointPath) { + this.checkpointPath = checkpointPath; + } + @Override public String getPath() { return this.path; @@ -178,6 +210,14 @@ public void setAccountId(long accountId) { this.accountId = accountId; } + public DataStoreTO getParentStore() { + return parentStore; + } + + public void setParentStore(DataStoreTO parentStore) { + this.parentStore = parentStore; + } + @Override public String toString() { return new StringBuilder("SnapshotTO[datastore=").append(dataStore).append("|volume=").append(volume).append("|path").append(path).append("]").toString(); diff --git a/core/src/main/java/org/apache/cloudstack/storage/to/TemplateObjectTO.java b/core/src/main/java/org/apache/cloudstack/storage/to/TemplateObjectTO.java index a405785bf64a..dc68b31a3fd1 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/to/TemplateObjectTO.java +++ b/core/src/main/java/org/apache/cloudstack/storage/to/TemplateObjectTO.java @@ -27,8 +27,9 @@ import com.cloud.hypervisor.Hypervisor; import com.cloud.storage.Storage.ImageFormat; import com.cloud.template.VirtualMachineTemplate; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; -public class TemplateObjectTO implements DataTO { +public class TemplateObjectTO extends DownloadableObjectTO implements DataTO { private String path; private String origUrl; private String uuid; @@ -87,6 +88,7 @@ public TemplateObjectTO(TemplateInfo template) { this.deployAsIs = template.isDeployAsIs(); this.deployAsIsConfiguration = template.getDeployAsIsConfiguration(); this.directDownload = template.isDirectDownload(); + this.followRedirects = template.isFollowRedirects(); } @Override @@ -263,6 +265,8 @@ public void setDeployAsIsConfiguration(String deployAsIsConfiguration) { @Override public String toString() { - return new StringBuilder("TemplateTO[id=").append(id).append("|origUrl=").append(origUrl).append("|name").append(name).append("]").toString(); + return String.format("TemplateTO %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "origUrl")); } } 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 8473ea7a49e7..827403ac5ef8 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 @@ -30,15 +30,19 @@ import com.cloud.storage.MigrationOptions; import com.cloud.storage.Storage; import com.cloud.storage.Volume; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import java.util.Arrays; +import java.util.List; +import java.util.Set; -public class VolumeObjectTO implements DataTO { +public class VolumeObjectTO extends DownloadableObjectTO implements DataTO { private String uuid; private Volume.Type volumeType; private DataStoreTO dataStore; private String name; private Long size; + private Long usableSize; private String path; private Long volumeId; private String vmName; @@ -74,6 +78,8 @@ public class VolumeObjectTO implements DataTO { @LogLevel(LogLevel.Log4jLevel.Off) private byte[] passphrase; private String encryptFormat; + private List checkpointPaths; + private Set checkpointImageStoreUrls; public VolumeObjectTO() { @@ -110,8 +116,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(); @@ -119,6 +125,9 @@ public VolumeObjectTO(VolumeInfo volume) { this.vSphereStoragePolicyId = volume.getvSphereStoragePolicyId(); this.passphrase = volume.getPassphrase(); this.encryptFormat = volume.getEncryptFormat(); + this.followRedirects = volume.isFollowRedirects(); + this.checkpointPaths = volume.getCheckpointPaths(); + this.checkpointImageStoreUrls = volume.getCheckpointImageStoreUrls(); } public String getUuid() { @@ -160,6 +169,10 @@ public Long getSize() { return size; } + public Long getUsableSize() { + return usableSize; + } + @Override public DataObjectType getObjectType() { return DataObjectType.VOLUME; @@ -177,6 +190,10 @@ public void setSize(long size) { this.size = size; } + public void setUsableSize(Long usableSize) { + this.usableSize = usableSize; + } + public void setPath(String path) { this.path = path; } @@ -248,7 +265,9 @@ public void setPoolId(Long poolId){ @Override public String toString() { - return new StringBuilder("volumeTO[uuid=").append(uuid).append("|path=").append(path).append("|datastore=").append(dataStore).append("]").toString(); + return String.format("volumeTO %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "path", "dataStore")); } public void setBytesReadRate(Long bytesReadRate) { @@ -324,6 +343,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; } @@ -384,4 +407,21 @@ public void clearPassphrase() { public boolean requiresEncryption() { return passphrase != null && passphrase.length > 0; } + + + public List getCheckpointPaths() { + return checkpointPaths; + } + + public void setCheckpointPaths(List checkpointPaths) { + this.checkpointPaths = checkpointPaths; + } + + public Set getCheckpointImageStoreUrls() { + return checkpointImageStoreUrls; + } + + public void setCheckpointImageStoreUrls(Set checkpointImageStoreUrls) { + this.checkpointImageStoreUrls = checkpointImageStoreUrls; + } } diff --git a/core/src/main/java/org/apache/cloudstack/transport/HypervisorTypeAdaptor.java b/core/src/main/java/org/apache/cloudstack/transport/HypervisorTypeAdaptor.java new file mode 100644 index 000000000000..bc4d3c3a6e3b --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/transport/HypervisorTypeAdaptor.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.transport; + +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +import java.lang.reflect.Type; + +/** + * {@link HypervisorType} acts as extendable set of singleton objects and should return same result when used "==" + * or {@link Object#equals(Object)}. + * To support that, need to return existing object for a given name instead of creating new. + */ +public class HypervisorTypeAdaptor implements JsonDeserializer, JsonSerializer { + @Override + public HypervisorType deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + if (json instanceof JsonPrimitive && ((JsonPrimitive) json).isString()) { + return HypervisorType.valueOf(json.getAsString()); + } + return null; + } + + @Override + public JsonElement serialize(HypervisorType src, Type typeOfSrc, JsonSerializationContext context) { + String name = src.name(); + if (name == null) { + return new JsonNull(); + } + return new JsonPrimitive(name); + } +} diff --git a/core/src/main/resources/META-INF/cloudstack/allocator/spring-core-allocator-context.xml b/core/src/main/resources/META-INF/cloudstack/allocator/spring-core-allocator-context.xml index a0d1b4cfd435..9f04a7fb6186 100644 --- a/core/src/main/resources/META-INF/cloudstack/allocator/spring-core-allocator-context.xml +++ b/core/src/main/resources/META-INF/cloudstack/allocator/spring-core-allocator-context.xml @@ -27,7 +27,7 @@ http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" - > + > diff --git a/core/src/main/resources/META-INF/cloudstack/allocator/spring-core-lifecycle-allocator-context-inheritable.xml b/core/src/main/resources/META-INF/cloudstack/allocator/spring-core-lifecycle-allocator-context-inheritable.xml index ec3bb63aeb66..6d05f8583036 100644 --- a/core/src/main/resources/META-INF/cloudstack/allocator/spring-core-lifecycle-allocator-context-inheritable.xml +++ b/core/src/main/resources/META-INF/cloudstack/allocator/spring-core-lifecycle-allocator-context-inheritable.xml @@ -27,7 +27,7 @@ http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" - > + > diff --git a/core/src/main/resources/META-INF/cloudstack/api/spring-core-lifecycle-api-context-inheritable.xml b/core/src/main/resources/META-INF/cloudstack/api/spring-core-lifecycle-api-context-inheritable.xml index 91a35f18a893..995ed30eb5e6 100644 --- a/core/src/main/resources/META-INF/cloudstack/api/spring-core-lifecycle-api-context-inheritable.xml +++ b/core/src/main/resources/META-INF/cloudstack/api/spring-core-lifecycle-api-context-inheritable.xml @@ -62,12 +62,12 @@ - + - + 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 a36d12431551..01c568d78916 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 @@ -262,7 +262,7 @@ - + @@ -276,11 +276,11 @@ class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry"> - + - + @@ -288,10 +288,10 @@ + class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry"> - + @@ -339,7 +339,7 @@ class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry"> - @@ -350,4 +350,20 @@ + + + + + + + + + + + + + + + diff --git a/core/src/main/resources/META-INF/cloudstack/event/module.properties b/core/src/main/resources/META-INF/cloudstack/event/module.properties new file mode 100644 index 000000000000..ab1f88e98448 --- /dev/null +++ b/core/src/main/resources/META-INF/cloudstack/event/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=event +parent=core diff --git a/core/src/main/resources/META-INF/cloudstack/event/spring-core-lifecycle-event-context-inheritable.xml b/core/src/main/resources/META-INF/cloudstack/event/spring-core-lifecycle-event-context-inheritable.xml new file mode 100644 index 000000000000..63d11c65bacb --- /dev/null +++ b/core/src/main/resources/META-INF/cloudstack/event/spring-core-lifecycle-event-context-inheritable.xml @@ -0,0 +1,31 @@ + + + + + + + + diff --git a/core/src/main/resources/META-INF/cloudstack/kubernetes/spring-core-lifecycle-kubernetes-context-inheritable.xml b/core/src/main/resources/META-INF/cloudstack/kubernetes/spring-core-lifecycle-kubernetes-context-inheritable.xml index df1a4b5c2298..96a9a634bae8 100644 --- a/core/src/main/resources/META-INF/cloudstack/kubernetes/spring-core-lifecycle-kubernetes-context-inheritable.xml +++ b/core/src/main/resources/META-INF/cloudstack/kubernetes/spring-core-lifecycle-kubernetes-context-inheritable.xml @@ -25,8 +25,8 @@ > - - + + diff --git a/core/src/main/resources/META-INF/cloudstack/network/spring-core-lifecycle-network-context-inheritable.xml b/core/src/main/resources/META-INF/cloudstack/network/spring-core-lifecycle-network-context-inheritable.xml index 8dbaf6105811..e5c232267ae6 100644 --- a/core/src/main/resources/META-INF/cloudstack/network/spring-core-lifecycle-network-context-inheritable.xml +++ b/core/src/main/resources/META-INF/cloudstack/network/spring-core-lifecycle-network-context-inheritable.xml @@ -92,7 +92,7 @@ - + + + + + + diff --git a/core/src/main/resources/META-INF/cloudstack/storage/spring-lifecycle-storage-context-inheritable.xml b/core/src/main/resources/META-INF/cloudstack/storage/spring-lifecycle-storage-context-inheritable.xml index 2771dfe415f8..a1c9055fd701 100644 --- a/core/src/main/resources/META-INF/cloudstack/storage/spring-lifecycle-storage-context-inheritable.xml +++ b/core/src/main/resources/META-INF/cloudstack/storage/spring-lifecycle-storage-context-inheritable.xml @@ -52,7 +52,7 @@ - + - - + + + + + diff --git a/core/src/main/resources/META-INF/cloudstack/system/spring-core-system-context-inheritable.xml b/core/src/main/resources/META-INF/cloudstack/system/spring-core-system-context-inheritable.xml index 4a59e95de35e..89442fc12b48 100644 --- a/core/src/main/resources/META-INF/cloudstack/system/spring-core-system-context-inheritable.xml +++ b/core/src/main/resources/META-INF/cloudstack/system/spring-core-system-context-inheritable.xml @@ -27,7 +27,7 @@ http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" - > + > @@ -44,8 +44,8 @@ - - diff --git a/core/src/test/java/com/cloud/agent/api/GetStorageStatsAnswerTest.java b/core/src/test/java/com/cloud/agent/api/GetStorageStatsAnswerTest.java new file mode 100644 index 000000000000..44af83ada2df --- /dev/null +++ b/core/src/test/java/com/cloud/agent/api/GetStorageStatsAnswerTest.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.agent.api; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class GetStorageStatsAnswerTest { + + @Test + public void testDefaultConstructor() { + GetStorageStatsAnswer answer = new GetStorageStatsAnswer(); + + Assert.assertEquals(0, answer.getByteUsed()); + Assert.assertEquals(0, answer.getCapacityBytes()); + Assert.assertNull(answer.getCapacityIops()); + Assert.assertNull(answer.getUsedIops()); + } + + @Test + public void testConstructorWithCapacityAndUsedBytes() { + GetStorageStatsCommand mockCmd = new GetStorageStatsCommand(); + long capacityBytes = 1024L; + long usedBytes = 512L; + + GetStorageStatsAnswer answer = new GetStorageStatsAnswer(mockCmd, capacityBytes, usedBytes); + + Assert.assertEquals(capacityBytes, answer.getCapacityBytes()); + Assert.assertEquals(usedBytes, answer.getByteUsed()); + Assert.assertNull(answer.getCapacityIops()); + Assert.assertNull(answer.getUsedIops()); + } + + @Test + public void testConstructorWithIops() { + GetStorageStatsCommand mockCmd = new GetStorageStatsCommand(); + long capacityBytes = 2048L; + long usedBytes = 1024L; + Long capacityIops = 1000L; + Long usedIops = 500L; + + GetStorageStatsAnswer answer = new GetStorageStatsAnswer(mockCmd, capacityBytes, usedBytes, capacityIops, usedIops); + + Assert.assertEquals(capacityBytes, answer.getCapacityBytes()); + Assert.assertEquals(usedBytes, answer.getByteUsed()); + Assert.assertEquals(capacityIops, answer.getCapacityIops()); + Assert.assertEquals(usedIops, answer.getUsedIops()); + } + + @Test + public void testErrorConstructor() { + GetStorageStatsCommand mockCmd = new GetStorageStatsCommand(); + String errorDetails = "An error occurred"; + + GetStorageStatsAnswer answer = new GetStorageStatsAnswer(mockCmd, errorDetails); + + Assert.assertFalse(answer.getResult()); + Assert.assertEquals(errorDetails, answer.getDetails()); + Assert.assertEquals(0, answer.getCapacityBytes()); + Assert.assertEquals(0, answer.getByteUsed()); + Assert.assertNull(answer.getCapacityIops()); + Assert.assertNull(answer.getUsedIops()); + } +} diff --git a/core/src/test/java/com/cloud/agent/api/GetVolumesOnStorageAnswerTest.java b/core/src/test/java/com/cloud/agent/api/GetVolumesOnStorageAnswerTest.java new file mode 100644 index 000000000000..26948f2b8232 --- /dev/null +++ b/core/src/test/java/com/cloud/agent/api/GetVolumesOnStorageAnswerTest.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.agent.api; + +import com.cloud.hypervisor.Hypervisor; +import org.apache.cloudstack.storage.volume.VolumeOnStorageTO; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.ArrayList; +import java.util.List; + +public class GetVolumesOnStorageAnswerTest { + + private static String path = "path"; + private static String name = "name"; + private static String fullPath = "fullPath"; + private static String format = "qcow2"; + private static long size = 10; + private static long virtualSize = 20; + private static String encryptFormat = "LUKS"; + + private static GetVolumesOnStorageCommand command = Mockito.mock(GetVolumesOnStorageCommand.class); + + @Test + public void testGetVolumesOnStorageAnswer() { + VolumeOnStorageTO volumeOnStorageTO = new VolumeOnStorageTO(Hypervisor.HypervisorType.KVM, path, name, fullPath, + format, size, virtualSize); + volumeOnStorageTO.setQemuEncryptFormat(encryptFormat); + + List volumesOnStorageTO = new ArrayList<>(); + volumesOnStorageTO.add(volumeOnStorageTO); + + GetVolumesOnStorageAnswer answer = new GetVolumesOnStorageAnswer(command, volumesOnStorageTO); + List volumes = answer.getVolumes(); + + Assert.assertEquals(1, volumes.size()); + VolumeOnStorageTO volume = volumes.get(0); + + Assert.assertEquals(Hypervisor.HypervisorType.KVM, volume.getHypervisorType()); + Assert.assertEquals(path, volume.getPath()); + Assert.assertEquals(name, volume.getName()); + Assert.assertEquals(fullPath, volume.getFullPath()); + Assert.assertEquals(format, volume.getFormat()); + Assert.assertEquals(size, volume.getSize()); + Assert.assertEquals(virtualSize, volume.getVirtualSize()); + Assert.assertEquals(encryptFormat, volume.getQemuEncryptFormat()); + Assert.assertEquals(path, volume.getPath()); + } + + @Test + public void testGetVolumesOnStorageAnswer2() { + String details = "details"; + GetVolumesOnStorageAnswer answer = new GetVolumesOnStorageAnswer(command, false, details); + Assert.assertFalse(answer.getResult()); + Assert.assertEquals(details, answer.getDetails()); + } +} diff --git a/core/src/test/java/com/cloud/agent/api/GetVolumesOnStorageCommandTest.java b/core/src/test/java/com/cloud/agent/api/GetVolumesOnStorageCommandTest.java new file mode 100644 index 000000000000..7b8a91145716 --- /dev/null +++ b/core/src/test/java/com/cloud/agent/api/GetVolumesOnStorageCommandTest.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.StorageFilerTO; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +public class GetVolumesOnStorageCommandTest { + + final StorageFilerTO pool = Mockito.mock(StorageFilerTO.class); + + final String localPath = "localPath"; + final String volumePath = "volumePath"; + final String keyword = "keyword"; + + @Test + public void testGetVolumesOnStorageCommand() { + GetVolumesOnStorageCommand command = new GetVolumesOnStorageCommand(pool, volumePath, keyword); + + Assert.assertEquals(pool, command.getPool()); + Assert.assertEquals(volumePath, command.getVolumePath()); + Assert.assertEquals(keyword, command.getKeyword()); + Assert.assertFalse(command.executeInSequence()); + } +} diff --git a/core/src/test/java/com/cloud/agent/api/routing/SetBgpPeersAnswerTest.java b/core/src/test/java/com/cloud/agent/api/routing/SetBgpPeersAnswerTest.java new file mode 100644 index 000000000000..4cd15e4465a9 --- /dev/null +++ b/core/src/test/java/com/cloud/agent/api/routing/SetBgpPeersAnswerTest.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 com.cloud.agent.api.routing; + +import org.apache.cloudstack.network.BgpPeerTO; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.ArrayList; +import java.util.List; + +public class SetBgpPeersAnswerTest { + + @Test + public void testSetBgpPeersAnswer() { + + String good = "good"; + String[] results = new String[1]; + results[0] = good; + + BgpPeerTO bgpPeerTO = Mockito.mock(BgpPeerTO.class); + List bgpPeerTOs = new ArrayList<>(); + bgpPeerTOs.add(bgpPeerTO); + SetBgpPeersCommand command = new SetBgpPeersCommand(bgpPeerTOs); + + SetBgpPeersAnswer answer = new SetBgpPeersAnswer(command, true, results); + + Assert.assertNotNull(answer.getResults()); + Assert.assertEquals(1, answer.getResults().length); + Assert.assertEquals(good, answer.getResults()[0]); + } + + @Test + public void testSetBgpPeersAnswer2() { + SetBgpPeersAnswer answer = new SetBgpPeersAnswer(); + + Assert.assertNull(answer.getResults()); + } +} diff --git a/core/src/test/java/com/cloud/agent/api/routing/SetBgpPeersCommandTest.java b/core/src/test/java/com/cloud/agent/api/routing/SetBgpPeersCommandTest.java new file mode 100644 index 000000000000..882c3b9da301 --- /dev/null +++ b/core/src/test/java/com/cloud/agent/api/routing/SetBgpPeersCommandTest.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 com.cloud.agent.api.routing; + +import org.apache.cloudstack.network.BgpPeerTO; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.ArrayList; +import java.util.List; + +public class SetBgpPeersCommandTest { + + @Test + public void testSetBgpPeersCommand1() { + SetBgpPeersCommand command = new SetBgpPeersCommand(); + Assert.assertNull(command.getBpgPeers()); + } + + @Test + public void testSetBgpPeersCommand2() { + BgpPeerTO bgpPeerTO = Mockito.mock(BgpPeerTO.class); + + List bgpPeerTOs = new ArrayList<>(); + bgpPeerTOs.add(bgpPeerTO); + + SetBgpPeersCommand command = new SetBgpPeersCommand(bgpPeerTOs); + Assert.assertNotNull(command.getBpgPeers()); + Assert.assertEquals(1, command.getBpgPeers().length); + Assert.assertEquals(bgpPeerTO, command.getBpgPeers()[0]); + } +} 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 042bec9d2168..6d4c6234c424 100644 --- a/core/src/test/java/com/cloud/agent/resource/virtualnetwork/ConfigHelperTest.java +++ b/core/src/test/java/com/cloud/agent/resource/virtualnetwork/ConfigHelperTest.java @@ -57,6 +57,7 @@ import com.cloud.agent.resource.virtualnetwork.model.LoadBalancerRule; import com.cloud.agent.resource.virtualnetwork.model.LoadBalancerRules; import com.cloud.network.lb.LoadBalancingRule.LbDestination; +import com.cloud.network.lb.LoadBalancingRule.LbSslCert; import com.cloud.network.Networks.TrafficType; public class ConfigHelperTest { @@ -223,9 +224,12 @@ public void testDeleteIpAlias() { protected LoadBalancerConfigCommand generateLoadBalancerConfigCommand() { final List lbs = new ArrayList<>(); final List dests = new ArrayList<>(); + final LbSslCert lbSslCert = new LbSslCert("cert", "key", "password", "chain", "fingerprint", false); dests.add(new LbDestination(80, 8080, "10.1.10.2", false)); dests.add(new LbDestination(80, 8080, "10.1.10.2", true)); - lbs.add(new LoadBalancerTO(UUID.randomUUID().toString(), "64.10.1.10", 80, "tcp", "algo", false, false, false, dests)); + LoadBalancerTO loadBalancerTO = new LoadBalancerTO(UUID.randomUUID().toString(), "64.10.1.10", 80, "tcp", "algo", false, false, false, dests); + loadBalancerTO.setLbSslCert(lbSslCert); + lbs.add(loadBalancerTO); final LoadBalancerTO[] arrayLbs = new LoadBalancerTO[lbs.size()]; lbs.toArray(arrayLbs); diff --git a/core/src/test/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResourceTest.java b/core/src/test/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResourceTest.java index 201242564ba6..4196587cc3f2 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 diff --git a/core/src/test/java/com/cloud/agent/resource/virtualnetwork/facade/SetBgpPeersConfigItemTest.java b/core/src/test/java/com/cloud/agent/resource/virtualnetwork/facade/SetBgpPeersConfigItemTest.java new file mode 100644 index 000000000000..5f177c88abfc --- /dev/null +++ b/core/src/test/java/com/cloud/agent/resource/virtualnetwork/facade/SetBgpPeersConfigItemTest.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 com.cloud.agent.resource.virtualnetwork.facade; + +import com.cloud.agent.api.routing.SetBgpPeersCommand; +import com.cloud.agent.resource.virtualnetwork.ConfigItem; +import com.cloud.agent.resource.virtualnetwork.FileConfigItem; +import com.cloud.agent.resource.virtualnetwork.ScriptConfigItem; +import com.cloud.agent.resource.virtualnetwork.VRScripts; + +import org.apache.cloudstack.network.BgpPeerTO; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.ArrayList; +import java.util.List; + +public class SetBgpPeersConfigItemTest { + + + @Test + public void testSetBgpPeersConfigItem() { + BgpPeerTO bgpPeerTO = Mockito.mock(BgpPeerTO.class); + List bgpPeerTOs = new ArrayList<>(); + bgpPeerTOs.add(bgpPeerTO); + SetBgpPeersCommand command = new SetBgpPeersCommand(bgpPeerTOs); + + SetBgpPeersConfigItem setBgpPeersConfigItem = new SetBgpPeersConfigItem(); + + List configItems = setBgpPeersConfigItem.generateConfig(command); + Assert.assertNotNull(configItems); + + Assert.assertEquals(2, configItems.size()); + Assert.assertTrue(configItems.get(0) instanceof FileConfigItem); + Assert.assertTrue(configItems.get(1) instanceof ScriptConfigItem); + + Assert.assertEquals(VRScripts.CONFIG_PERSIST_LOCATION, ((FileConfigItem) configItems.get(0)).getFilePath()); + Assert.assertTrue((((FileConfigItem) configItems.get(0)).getFileName().startsWith(VRScripts.BGP_PEERS_CONFIG))); + Assert.assertEquals(VRScripts.UPDATE_CONFIG, ((ScriptConfigItem) configItems.get(1)).getScript()); + } +} diff --git a/core/src/test/java/com/cloud/agent/resource/virtualnetwork/model/BgpPeersTest.java b/core/src/test/java/com/cloud/agent/resource/virtualnetwork/model/BgpPeersTest.java new file mode 100644 index 000000000000..eba423e55ed8 --- /dev/null +++ b/core/src/test/java/com/cloud/agent/resource/virtualnetwork/model/BgpPeersTest.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 com.cloud.agent.resource.virtualnetwork.model; + +import org.apache.cloudstack.network.BgpPeerTO; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.ArrayList; +import java.util.List; + +public class BgpPeersTest { + + @Test + public void testBgpPeers() { + BgpPeerTO bgpPeerTO = Mockito.mock(BgpPeerTO.class); + List bgpPeerTOs = new ArrayList<>(); + bgpPeerTOs.add(bgpPeerTO); + + BgpPeers bgpPeers = new BgpPeers(bgpPeerTOs); + Assert.assertEquals(ConfigBase.BGP_PEERS, bgpPeers.getType()); + Assert.assertNotNull(bgpPeers.getPeers()); + Assert.assertEquals(1, bgpPeers.getPeers().size()); + Assert.assertEquals(bgpPeerTO, bgpPeers.getPeers().get(0)); + } + + @Test + public void testBgpPeers2() { + BgpPeers bgpPeers = new BgpPeers(); + Assert.assertEquals(ConfigBase.BGP_PEERS, bgpPeers.getType()); + + BgpPeerTO bgpPeerTO = Mockito.mock(BgpPeerTO.class); + List bgpPeerTOs = new ArrayList<>(); + bgpPeerTOs.add(bgpPeerTO); + bgpPeers.setPeers(bgpPeerTOs); + + Assert.assertNotNull(bgpPeers.getPeers()); + Assert.assertEquals(1, bgpPeers.getPeers().size()); + Assert.assertEquals(bgpPeerTO, bgpPeers.getPeers().get(0)); + } +} diff --git a/core/src/test/java/com/cloud/agent/resource/virtualnetwork/model/LoadBalancerRuleTest.java b/core/src/test/java/com/cloud/agent/resource/virtualnetwork/model/LoadBalancerRuleTest.java new file mode 100644 index 000000000000..f2b25a5f78ec --- /dev/null +++ b/core/src/test/java/com/cloud/agent/resource/virtualnetwork/model/LoadBalancerRuleTest.java @@ -0,0 +1,63 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.agent.resource.virtualnetwork.model; + +import org.junit.Assert; +import org.junit.Test; + +public class LoadBalancerRuleTest { + + @Test + public void testSslCertEntry() { + String name = "name"; + String cert = "cert"; + String key = "key1"; + String chain = "chain"; + String password = "password"; + LoadBalancerRule.SslCertEntry sslCertEntry = new LoadBalancerRule.SslCertEntry(name, cert, key, chain, password); + + Assert.assertEquals(name, sslCertEntry.getName()); + Assert.assertEquals(cert, sslCertEntry.getCert()); + Assert.assertEquals(key, sslCertEntry.getKey()); + Assert.assertEquals(chain, sslCertEntry.getChain()); + Assert.assertEquals(password, sslCertEntry.getPassword()); + + String name2 = "name2"; + String cert2 = "cert2"; + String key2 = "key2"; + String chain2 = "chain2"; + String password2 = "password2"; + + sslCertEntry.setName(name2); + sslCertEntry.setCert(cert2); + sslCertEntry.setKey(key2); + sslCertEntry.setChain(chain2); + sslCertEntry.setPassword(password2); + + Assert.assertEquals(name2, sslCertEntry.getName()); + Assert.assertEquals(cert2, sslCertEntry.getCert()); + Assert.assertEquals(key2, sslCertEntry.getKey()); + Assert.assertEquals(chain2, sslCertEntry.getChain()); + Assert.assertEquals(password2, sslCertEntry.getPassword()); + + LoadBalancerRule loadBalancerRule = new LoadBalancerRule(); + loadBalancerRule.setSslCerts(new LoadBalancerRule.SslCertEntry[]{sslCertEntry}); + + Assert.assertEquals(1, loadBalancerRule.getSslCerts().length); + Assert.assertEquals(sslCertEntry, loadBalancerRule.getSslCerts()[0]); + } +} diff --git a/core/src/test/java/com/cloud/agent/transport/ResponseTest.java b/core/src/test/java/com/cloud/agent/transport/ResponseTest.java new file mode 100644 index 000000000000..06869b42eb9d --- /dev/null +++ b/core/src/test/java/com/cloud/agent/transport/ResponseTest.java @@ -0,0 +1,46 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package com.cloud.agent.transport; + +import junit.framework.TestCase; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.junit.Assert; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.CheckS2SVpnConnectionsAnswer; + +import com.cloud.agent.transport.Request.Version; + +public class ResponseTest extends TestCase { + protected Logger logger = LogManager.getLogger(getClass()); + + public void testCheckS2SVpnConnectionsAnswer() { + logger.info("Testing CheckS2SVpnConnectionsAnswer"); + String content = "[{\"com.cloud.agent.api.CheckS2SVpnConnectionsAnswer\":{\"ipToConnected\":{\"10.0.53.13\":true}," + + "\"ipToDetail\":{\"10.0.53.13\":\"IPsec SA found;Site-to-site VPN have connected\"}," + + "\"details\":\"10.0.53.13:0:IPsec SA found;Site-to-site VPN have connected\\u0026\\n\"," + + "\"result\":true,\"contextMap\":{},\"wait\":0,\"bypassHostMaintenance\":false}}]"; + Response response = new Response(Version.v2, 1L, 2L, 3L, 1L, (short)1, content); + Answer answer = response.getAnswer(); + Assert.assertTrue(answer instanceof CheckS2SVpnConnectionsAnswer); + } + +} diff --git a/core/src/test/java/com/cloud/network/HAProxyConfiguratorTest.java b/core/src/test/java/com/cloud/network/HAProxyConfiguratorTest.java index 2a282cbeca8b..72361c2880ed 100644 --- a/core/src/test/java/com/cloud/network/HAProxyConfiguratorTest.java +++ b/core/src/test/java/com/cloud/network/HAProxyConfiguratorTest.java @@ -31,6 +31,7 @@ import com.cloud.agent.api.routing.LoadBalancerConfigCommand; import com.cloud.agent.api.to.LoadBalancerTO; import com.cloud.network.lb.LoadBalancingRule.LbDestination; +import com.cloud.network.lb.LoadBalancingRule.LbSslCert; import java.util.List; import java.util.ArrayList; @@ -80,11 +81,11 @@ public void testGenerateConfigurationLoadBalancerConfigCommand() { HAProxyConfigurator hpg = new HAProxyConfigurator(); LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "12", false); String result = genConfig(hpg, cmd); - assertTrue("keepalive disabled should result in 'mode http' in the resulting haproxy config", result.contains("mode http")); + assertTrue("keepalive disabled should result in 'option httpclose' in the resulting haproxy config", result.contains("\toption httpclose")); cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "4", true); result = genConfig(hpg, cmd); - assertTrue("keepalive enabled should not result in 'mode http' in the resulting haproxy config", !result.contains("mode http")); + assertTrue("keepalive enabled should result in 'no option httpclose' in the resulting haproxy config", result.contains("\tno option httpclose")); // TODO // create lb command // setup tests for @@ -122,6 +123,19 @@ public void generateConfigurationTestWithCidrList() { Assert.assertTrue(result.contains("acl network_allowed src 1.1.1.1 2.2.2.2/24 \n\ttcp-request connection reject if !network_allowed")); } + @Test + public void generateConfigurationTestWithSslCert() { + LoadBalancerTO lb = new LoadBalancerTO("1", "10.2.0.1", 443, "ssl", "roundrobin", false, false, false, null); + final LbSslCert lbSslCert = new LbSslCert("cert", "key", "password", "chain", "fingerprint", false); + lb.setLbSslCert(lbSslCert); + LoadBalancerTO[] lba = new LoadBalancerTO[1]; + lba[0] = lb; + HAProxyConfigurator hpg = new HAProxyConfigurator(); + LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "12", false); + String result = genConfig(hpg, cmd); + Assert.assertTrue(result.contains("bind 10.2.0.1:443 ssl crt /etc/cloudstack/ssl/10_2_0_1-443.pem")); + } + private String genConfig(HAProxyConfigurator hpg, LoadBalancerConfigCommand cmd) { String[] sa = hpg.generateConfiguration(cmd); StringBuilder sb = new StringBuilder(); diff --git a/core/src/test/java/com/cloud/serializer/GsonHelperTest.java b/core/src/test/java/com/cloud/serializer/GsonHelperTest.java new file mode 100644 index 000000000000..e8b0b373060b --- /dev/null +++ b/core/src/test/java/com/cloud/serializer/GsonHelperTest.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.serializer; + +import com.cloud.agent.api.to.NfsTO; +import com.cloud.storage.DataStoreRole; +import com.google.gson.Gson; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * Test cases to verify working order of GsonHelper.java + * with regards to a concrete implementation of the DataStoreTO + * interface + */ +public class GsonHelperTest { + + private Gson gson; + private Gson gsonLogger; + private NfsTO nfsTO; + + @Before + public void setUp() { + gson = GsonHelper.getGson(); + gsonLogger = GsonHelper.getGsonLogger(); + nfsTO = new NfsTO("http://example.com", DataStoreRole.Primary); + } + + @Test + public void testGsonSerialization() { + String json = gson.toJson(nfsTO); + assertNotNull(json); + assertTrue(json.contains("\"_url\":\"http://example.com\"")); + assertTrue(json.contains("\"_role\":\"Primary\"")); + } + + @Test + public void testGsonDeserialization() { + String json = "{\"_url\":\"http://example.com\",\"_role\":\"Primary\"}"; + NfsTO deserializedNfsTO = gson.fromJson(json, NfsTO.class); + assertNotNull(deserializedNfsTO); + assertEquals("http://example.com", deserializedNfsTO.getUrl()); + assertEquals(DataStoreRole.Primary, deserializedNfsTO.getRole()); + } + + @Test + public void testGsonLoggerSerialization() { + String json = gsonLogger.toJson(nfsTO); + assertNotNull(json); + assertTrue(json.contains("\"_url\":\"http://example.com\"")); + assertTrue(json.contains("\"_role\":\"Primary\"")); + } + + @Test + public void testGsonLoggerDeserialization() { + String json ="{\"_url\":\"http://example.com\",\"_role\":\"Primary\"}"; + NfsTO deserializedNfsTO = gsonLogger.fromJson(json, NfsTO.class); + assertNotNull(deserializedNfsTO); + assertEquals("http://example.com", deserializedNfsTO.getUrl()); + assertEquals(DataStoreRole.Primary, deserializedNfsTO.getRole()); + } +} diff --git a/core/src/test/java/com/cloud/storage/template/OVAProcessorTest.java b/core/src/test/java/com/cloud/storage/template/OVAProcessorTest.java index 8ab546447182..8674a8df2861 100644 --- a/core/src/test/java/com/cloud/storage/template/OVAProcessorTest.java +++ b/core/src/test/java/com/cloud/storage/template/OVAProcessorTest.java @@ -131,5 +131,25 @@ public void testGetVirtualSize() throws Exception { Assert.assertEquals(virtualSize, processor.getVirtualSize(mockFile)); Mockito.verify(mockFile, Mockito.times(0)).length(); } + @Test + public void testProcessWithLargeFileSize() throws Exception { + String templatePath = "/tmp"; + String templateName = "large_template"; + long virtualSize = 10_000_000_000L; + long actualSize = 5_000_000_000L; + + Mockito.when(mockStorageLayer.exists(Mockito.anyString())).thenReturn(true); + Mockito.when(mockStorageLayer.getSize(Mockito.anyString())).thenReturn(actualSize); + Mockito.doReturn(virtualSize).when(processor).getTemplateVirtualSize(Mockito.anyString(), Mockito.anyString()); + try (MockedConstruction + @@ -87,25 +96,29 @@ execute - - def csVersion = pom.properties['cs.version'] - def patch = pom.properties['patch.version'] - def templateList = [] - templateList.add("systemvmtemplate-${csVersion}.${patch}-kvm") - templateList.add("systemvmtemplate-${csVersion}.${patch}-vmware") - templateList.add("systemvmtemplate-${csVersion}.${patch}-xen") - templateList.add("systemvmtemplate-${csVersion}.${patch}-ovm") - templateList.add("systemvmtemplate-${csVersion}.${patch}-hyperv") - File file = new File("./engine/schema/dist/systemvm-templates/md5sum.txt") - def lines = file.readLines() - for (template in templateList) { - def data = lines.findAll { it.contains(template) } - if (data != null) { - def hypervisor = template.tokenize('-')[-1] - pom.properties["$hypervisor" + ".checksum"] = data[0].tokenize(' ')[0] + + + @@ -122,7 +135,7 @@ wget - ${project.systemvm.template.location}/${cs.version}/md5sum.txt + ${project.systemvm.template.location}/${cs.version}/sha512sum.txt ${basedir}/dist/systemvm-templates/ true true @@ -133,7 +146,7 @@ org.codehaus.mojo exec-maven-plugin - 1.2.1 + ${cs.exec-maven-plugin.version} systemvm-template-metadata @@ -152,6 +165,15 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + + true + + + @@ -181,9 +203,9 @@ true - ${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-kvm.qcow2.bz2 + ${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-x86_64-kvm.qcow2.bz2 ${basedir}/dist/systemvm-templates/ - ${kvm.checksum} + ${kvm.checksum} @@ -217,9 +239,9 @@ true - ${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-vmware.ova + ${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-x86_64-vmware.ova ${basedir}/dist/systemvm-templates/ - ${vmware.checksum} + ${vmware.checksum} @@ -253,9 +275,9 @@ true - ${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-xen.vhd.bz2 + ${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-x86_64-xen.vhd.bz2 ${basedir}/dist/systemvm-templates/ - ${xen.checksum} + ${xen.checksum} @@ -289,9 +311,9 @@ true - ${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-ovm.raw.bz2 + ${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-x86_64-ovm.raw.bz2 ${basedir}/dist/systemvm-templates/ - ${ovm.checksum} + ${ovm.checksum} @@ -325,9 +347,9 @@ true - ${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-hyperv.vhd.zip + ${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-x86_64-hyperv.vhd.zip ${basedir}/dist/systemvm-templates/ - ${hyperv.checksum} + ${hyperv.checksum} diff --git a/engine/schema/src/main/java/com/cloud/capacity/CapacityVO.java b/engine/schema/src/main/java/com/cloud/capacity/CapacityVO.java index 50c40134a911..fb2d61d8e11a 100644 --- a/engine/schema/src/main/java/com/cloud/capacity/CapacityVO.java +++ b/engine/schema/src/main/java/com/cloud/capacity/CapacityVO.java @@ -80,6 +80,9 @@ public class CapacityVO implements Capacity { @Transient private Long allocatedCapacity; + @Transient + private String tag; + public CapacityVO() { } @@ -132,8 +135,8 @@ public Long getPodId() { return podId; } - public void setPodId(long podId) { - this.podId = new Long(podId); + public void setPodId(Long podId) { + this.podId = podId; } @Override @@ -141,8 +144,8 @@ public Long getClusterId() { return clusterId; } - public void setClusterId(long clusterId) { - this.clusterId = new Long(clusterId); + public void setClusterId(Long clusterId) { + this.clusterId = clusterId; } @Override @@ -221,6 +224,15 @@ public void setAllocatedCapacity(Long allocatedCapacity) { this.allocatedCapacity = allocatedCapacity; } + @Override + public String getTag() { + return tag; + } + + public void setTag(String tag) { + this.tag = tag; + } + @Override public String getUuid() { return null; //To change body of implemented methods use File | Settings | File Templates. @@ -242,6 +254,8 @@ public String getUuid() { capacityNames.put(CAPACITY_TYPE_GPU, "GPU"); capacityNames.put(CAPACITY_TYPE_CPU_CORE, "CPU_CORE"); capacityNames.put(CAPACITY_TYPE_VIRTUAL_NETWORK_IPV6_SUBNET, "VIRTUAL_NETWORK_IPV6_SUBNET"); + capacityNames.put(CAPACITY_TYPE_BACKUP_STORAGE, "BACKUP_STORAGE"); + capacityNames.put(CAPACITY_TYPE_OBJECT_STORAGE, "OBJECT_STORAGE"); } public static String getCapacityName (Short capacityType) { diff --git a/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDao.java b/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDao.java index 459a63a7ba13..42313efa512f 100644 --- a/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDao.java +++ b/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDao.java @@ -28,7 +28,9 @@ public interface CapacityDao extends GenericDao { CapacityVO findByHostIdType(Long hostId, short capacityType); - List listClustersInZoneOrPodByHostCapacities(long id, long vmId, int requiredCpu, long requiredRam, short capacityTypeForOrdering, boolean isZone); + List listByHostIdTypes(Long hostId, List capacityTypes); + + List listClustersInZoneOrPodByHostCapacities(long id, long vmId, int requiredCpu, long requiredRam, boolean isZone); List listHostsWithEnoughCapacity(int requiredCpu, long requiredRam, Long clusterId, String hostType); @@ -44,14 +46,17 @@ public interface CapacityDao extends GenericDao { List findCapacityBy(Integer capacityType, Long zoneId, Long podId, Long clusterId); - List listPodsByHostCapacities(long zoneId, int requiredCpu, long requiredRam, short capacityType); + List findFilteredCapacityBy(Integer capacityType, Long zoneId, Long podId, Long clusterId, List hostIds, List poolIds); + + List listPodsByHostCapacities(long zoneId, int requiredCpu, long requiredRam); Pair, Map> orderPodsByAggregateCapacity(long zoneId, short capacityType); List findCapacityBy(Integer capacityType, Long zoneId, Long podId, Long clusterId, String resourceState); - List listCapacitiesGroupedByLevelAndType(Integer capacityType, Long zoneId, Long podId, Long clusterId, int level, Long limit); + List listCapacitiesGroupedByLevelAndType(Integer capacityType, Long zoneId, Long podId, + Long clusterId, int level, List hostIds, List poolIds, Long limit); void updateCapacityState(Long dcId, Long podId, Long clusterId, Long hostId, String capacityState, short[] capacityType); @@ -59,5 +64,11 @@ List findCapacityBy(Integer capacityType, Long zoneId, float findClusterConsumption(Long clusterId, short capacityType, long computeRequested); - List orderHostsByFreeCapacity(Long zoneId, Long clusterId, short capacityType); + Pair, Map> orderHostsByFreeCapacity(Long zoneId, Long clusterId, short capacityType); + + List listHostCapacityByCapacityTypes(Long zoneId, Long clusterId, List capacityTypes); + + List listPodCapacityByCapacityTypes(Long zoneId, List capacityTypes); + + List listClusterCapacityByCapacityTypes(Long zoneId, Long podId, List capacityTypes); } diff --git a/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDaoImpl.java b/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDaoImpl.java index 67d037319d5b..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 @@ -26,11 +26,11 @@ import javax.inject.Inject; -import org.apache.commons.lang3.StringUtils; -import org.springframework.stereotype.Component; - import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; import com.cloud.capacity.Capacity; import com.cloud.capacity.CapacityVO; @@ -204,15 +204,17 @@ public class CapacityDaoImpl extends GenericDaoBase implements "(CASE WHEN ISNULL(service_offering.speed) THEN custom_speed.value ELSE service_offering.speed end) AS speed, " + "(CASE WHEN ISNULL(service_offering.ram_size) THEN custom_ram_size.value ELSE service_offering.ram_size end) AS ram_size " + "FROM vm_instance vi LEFT JOIN service_offering ON(((vi.service_offering_id = service_offering.id))) " + - "LEFT JOIN user_vm_details custom_cpu ON(((custom_cpu.vm_id = vi.id) AND (custom_cpu.name = 'CpuNumber'))) " + - "LEFT JOIN user_vm_details custom_speed ON(((custom_speed.vm_id = vi.id) AND (custom_speed.name = 'CpuSpeed'))) " + - "LEFT JOIN user_vm_details custom_ram_size ON(((custom_ram_size.vm_id = vi.id) AND (custom_ram_size.name = 'memory'))) "; + "LEFT JOIN vm_instance_details custom_cpu ON(((custom_cpu.vm_id = vi.id) AND (custom_cpu.name = 'CpuNumber'))) " + + "LEFT JOIN vm_instance_details custom_speed ON(((custom_speed.vm_id = vi.id) AND (custom_speed.name = 'CpuSpeed'))) " + + "LEFT JOIN vm_instance_details custom_ram_size ON(((custom_ram_size.vm_id = vi.id) AND (custom_ram_size.name = 'memory'))) "; private static final String WHERE_STATE_IS_NOT_DESTRUCTIVE = "WHERE ISNULL(vi.removed) AND vi.state NOT IN ('Destroyed', 'Error', 'Expunging')"; 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); @@ -339,7 +341,8 @@ public List findCapacityBy(Integer capacityType, Long zoneId, Lo } @Override - public List listCapacitiesGroupedByLevelAndType(Integer capacityType, Long zoneId, Long podId, Long clusterId, int level, Long limit) { + public List listCapacitiesGroupedByLevelAndType(Integer capacityType, Long zoneId, Long podId, + Long clusterId, int level, List hostIds, List poolIds, Long limit) { StringBuilder finalQuery = new StringBuilder(); TransactionLegacy txn = TransactionLegacy.currentTxn(); @@ -379,6 +382,23 @@ public List listCapacitiesGroupedByLevelAndType(Integer capacity 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) { + finalQuery.append(String.format(" AND capacity_type NOT IN (%s)", StringUtils.join(Capacity.STORAGE_CAPACITY_TYPES, ","))); + } + } + if (CollectionUtils.isNotEmpty(poolIds)) { + finalQuery.append(String.format(" AND capacity.host_id IN (%s)", StringUtils.join(poolIds, ","))); + if (capacityType == null) { + finalQuery.append(String.format(" AND capacity_type IN (%s)", StringUtils.join(Capacity.STORAGE_CAPACITY_TYPES, ","))); + } + } + switch (level) { case 1: // List all the capacities grouped by zone, capacity Type finalQuery.append(LIST_CAPACITY_GROUP_BY_ZONE_TYPE_PART2); @@ -461,8 +481,37 @@ public Ternary findCapacityByZoneAndHostTag(Long zoneId, Strin } } + protected String getHostAndPoolConditionForFilteredCapacity(Integer capacityType, List hostIds, List poolIds) { + StringBuilder sql = new StringBuilder(); + if (CollectionUtils.isEmpty(hostIds) && CollectionUtils.isEmpty(poolIds)) { + return ""; + } + sql.append(" AND ("); + boolean hostConditionAdded = false; + if (CollectionUtils.isNotEmpty(hostIds) && (capacityType == null || !Capacity.STORAGE_CAPACITY_TYPES.contains(capacityType.shortValue()))) { + sql.append(String.format("(capacity.host_id IN (%s)", StringUtils.join(hostIds, ","))); + if (capacityType == null) { + sql.append(String.format(" AND capacity_type NOT IN (%s)", StringUtils.join(Capacity.STORAGE_CAPACITY_TYPES, ","))); + } + sql.append(")"); + hostConditionAdded = true; + } + if (CollectionUtils.isNotEmpty(poolIds) && (capacityType == null || Capacity.STORAGE_CAPACITY_TYPES.contains(capacityType.shortValue()))) { + if (hostConditionAdded) { + sql.append(" OR "); + } + sql.append(String.format("(capacity.host_id IN (%s)", StringUtils.join(poolIds, ","))); + if (capacityType == null || Capacity.STORAGE_CAPACITY_TYPES.contains(capacityType.shortValue())) { + sql.append(String.format(" AND capacity_type IN (%s)", StringUtils.join(Capacity.STORAGE_CAPACITY_TYPES, ","))); + } + sql.append(")"); + } + sql.append(")"); + return sql.toString(); + } + @Override - public List findCapacityBy(Integer capacityType, Long zoneId, Long podId, Long clusterId) { + public List findFilteredCapacityBy(Integer capacityType, Long zoneId, Long podId, Long clusterId, List hostIds, List poolIds) { TransactionLegacy txn = TransactionLegacy.currentTxn(); PreparedStatement pstmt = null; @@ -499,6 +548,10 @@ public List findCapacityBy(Integer capacityType, Long zoneId, Lo 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); @@ -516,6 +569,8 @@ public List findCapacityBy(Integer capacityType, Long zoneId, Lo resourceIdList.add(capacityType.longValue()); } + sql.append(getHostAndPoolConditionForFilteredCapacity(capacityType, hostIds, poolIds)); + if (podId == null && clusterId == null) { sql.append(" GROUP BY capacity_type, data_center_id"); } else { @@ -591,6 +646,11 @@ public List findCapacityBy(Integer capacityType, Long zoneId, Lo } } + @Override + public List findCapacityBy(Integer capacityType, Long zoneId, Long podId, Long clusterId) { + return findFilteredCapacityBy(capacityType, zoneId, podId, clusterId, null, null); + } + public void updateAllocated(Long hostId, long allocatedAmount, short capacityType, boolean add) { TransactionLegacy txn = TransactionLegacy.currentTxn(); PreparedStatement pstmt = null; @@ -623,7 +683,19 @@ public CapacityVO findByHostIdType(Long hostId, short capacityType) { } @Override - public List listClustersInZoneOrPodByHostCapacities(long id, long vmId, int requiredCpu, long requiredRam, short capacityTypeForOrdering, boolean isZone) { + public List listByHostIdTypes(Long hostId, List capacityTypes) { + SearchBuilder sb = createSearchBuilder(); + sb.and("hostId", sb.entity().getHostOrPoolId(), SearchCriteria.Op.EQ); + sb.and("type", sb.entity().getCapacityType(), SearchCriteria.Op.IN); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("hostId", hostId); + sc.setParameters("type", capacityTypes.toArray()); + return listBy(sc); + } + + @Override + public List listClustersInZoneOrPodByHostCapacities(long id, long vmId, int requiredCpu, long requiredRam, boolean isZone) { TransactionLegacy txn = TransactionLegacy.currentTxn(); PreparedStatement pstmt = null; List result = new ArrayList(); @@ -702,6 +774,7 @@ public static class SummedCapacity { public Long clusterId; public Long podId; public Long dcId; + public String tag; public SummedCapacity() { } @@ -790,6 +863,14 @@ public Long getAllocatedCapacity() { public void setAllocatedCapacity(Long sumAllocated) { this.sumAllocated = sumAllocated; } + + public String getTag() { + return tag; + } + + public void setTag(String tag) { + this.tag = tag; + } } @Override @@ -958,10 +1039,11 @@ public Pair, Map> orderClustersByAggregateCapacity(long } @Override - public List orderHostsByFreeCapacity(Long zoneId, Long clusterId, short capacityTypeForOrdering){ + public Pair, Map> orderHostsByFreeCapacity(Long zoneId, Long clusterId, short capacityTypeForOrdering){ TransactionLegacy txn = TransactionLegacy.currentTxn(); PreparedStatement pstmt = null; - List result = new ArrayList(); + List result = new ArrayList<>(); + Map hostCapacityMap = new HashMap<>(); StringBuilder sql = new StringBuilder(ORDER_HOSTS_BY_FREE_CAPACITY_PART1); if (zoneId != null) { sql.append(" AND data_center_id = ?"); @@ -984,9 +1066,11 @@ public List orderHostsByFreeCapacity(Long zoneId, Long clusterId, short ca ResultSet rs = pstmt.executeQuery(); while (rs.next()) { - result.add(rs.getLong(1)); + Long hostId = rs.getLong(1); + result.add(hostId); + hostCapacityMap.put(hostId, rs.getDouble(2)); } - return result; + return new Pair<>(result, hostCapacityMap); } catch (SQLException e) { throw new CloudRuntimeException("DB Exception on: " + sql, e); } catch (Throwable e) { @@ -995,7 +1079,65 @@ public List orderHostsByFreeCapacity(Long zoneId, Long clusterId, short ca } @Override - public List listPodsByHostCapacities(long zoneId, int requiredCpu, long requiredRam, short capacityType) { + public List listHostCapacityByCapacityTypes(Long zoneId, Long clusterId, List capacityTypes) { + SearchBuilder sb = createSearchBuilder(); + sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); + sb.and("clusterId", sb.entity().getClusterId(), SearchCriteria.Op.EQ); + sb.and("capacityTypes", sb.entity().getCapacityType(), SearchCriteria.Op.IN); + sb.and("capacityState", sb.entity().getCapacityState(), Op.EQ); + sb.done(); + + SearchCriteria sc = sb.create(); + sc.setParameters("capacityState", "Enabled"); + if (zoneId != null) { + sc.setParameters("zoneId", zoneId); + } + if (clusterId != null) { + sc.setParameters("clusterId", clusterId); + } + sc.setParameters("capacityTypes", capacityTypes.toArray()); + return listBy(sc); + } + + @Override + public List listPodCapacityByCapacityTypes(Long zoneId, List capacityTypes) { + SearchBuilder sb = createSearchBuilder(); + sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); + sb.and("capacityTypes", sb.entity().getCapacityType(), SearchCriteria.Op.IN); + sb.and("capacityState", sb.entity().getCapacityState(), Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("capacityState", "Enabled"); + if (zoneId != null) { + sc.setParameters("zoneId", zoneId); + } + sc.setParameters("capacityTypes", capacityTypes.toArray()); + return listBy(sc); + } + + @Override + public List listClusterCapacityByCapacityTypes(Long zoneId, Long podId, List capacityTypes) { + SearchBuilder sb = createSearchBuilder(); + sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); + sb.and("podId", sb.entity().getPodId(), SearchCriteria.Op.EQ); + sb.and("capacityTypes", sb.entity().getCapacityType(), SearchCriteria.Op.IN); + sb.and("capacityState", sb.entity().getCapacityState(), Op.EQ); + sb.done(); + + SearchCriteria sc = sb.create(); + sc.setParameters("capacityState", "Enabled"); + if (zoneId != null) { + sc.setParameters("zoneId", zoneId); + } + if (podId != null) { + sc.setParameters("podId", podId); + } + sc.setParameters("capacityTypes", capacityTypes.toArray()); + return listBy(sc); + } + + @Override + public List listPodsByHostCapacities(long zoneId, int requiredCpu, long requiredRam) { TransactionLegacy txn = TransactionLegacy.currentTxn(); PreparedStatement pstmt = null; List result = new ArrayList(); diff --git a/engine/schema/src/main/java/com/cloud/configuration/ManagementServiceConfiguration.java b/engine/schema/src/main/java/com/cloud/configuration/ManagementServiceConfiguration.java index 51b7f62f56de..841447de5fd8 100644 --- a/engine/schema/src/main/java/com/cloud/configuration/ManagementServiceConfiguration.java +++ b/engine/schema/src/main/java/com/cloud/configuration/ManagementServiceConfiguration.java @@ -21,7 +21,7 @@ public interface ManagementServiceConfiguration extends Configurable { ConfigKey PingInterval = new ConfigKey("Advanced", Integer.class, "ping.interval", "60", - "Interval to send application level pings to make sure the connection is still working", false); + "Interval in seconds to send application level pings to make sure the connection is still working", false); ConfigKey PingTimeout = new ConfigKey("Advanced", Float.class, "ping.timeout", "2.5", "Multiplier to ping.interval before announcing an agent has timed out", true); public int getPingInterval(); diff --git a/engine/schema/src/main/java/com/cloud/configuration/ResourceCountVO.java b/engine/schema/src/main/java/com/cloud/configuration/ResourceCountVO.java index ae8f3822704c..9e39a608f9ec 100644 --- a/engine/schema/src/main/java/com/cloud/configuration/ResourceCountVO.java +++ b/engine/schema/src/main/java/com/cloud/configuration/ResourceCountVO.java @@ -47,10 +47,13 @@ public class ResourceCountVO implements ResourceCount { @Column(name = "count") private long count; + @Column(name = "tag") + private String tag; + public ResourceCountVO() { } - public ResourceCountVO(ResourceType type, long count, long ownerId, ResourceOwnerType ownerType) { + public ResourceCountVO(ResourceType type, long count, long ownerId, ResourceOwnerType ownerType, String tag) { this.type = type; this.count = count; @@ -59,6 +62,11 @@ public ResourceCountVO(ResourceType type, long count, long ownerId, ResourceOwne } else if (ownerType == ResourceOwnerType.Domain) { this.domainId = ownerId; } + this.tag = tag; + } + + public ResourceCountVO(ResourceType type, long count, long ownerId, ResourceOwnerType ownerType) { + this(type, count, ownerId, ownerType, null); } @Override @@ -99,7 +107,7 @@ public Long getAccountId() { @Override public String toString() { - return new StringBuilder("REsourceCount[").append("-") + return new StringBuilder("ResourceCount[").append("-") .append(id) .append("-") .append(type) @@ -107,6 +115,8 @@ public String toString() { .append(accountId) .append("-") .append(domainId) + .append("-") + .append(tag) .append("]") .toString(); } @@ -136,4 +146,13 @@ public void setDomainId(Long domainId) { public void setAccountId(Long accountId) { this.accountId = accountId; } + + @Override + public String getTag() { + return tag; + } + + public void setTag(String tag) { + this.tag = tag; + } } diff --git a/engine/schema/src/main/java/com/cloud/configuration/ResourceLimitVO.java b/engine/schema/src/main/java/com/cloud/configuration/ResourceLimitVO.java index 392170919757..1619537ae744 100644 --- a/engine/schema/src/main/java/com/cloud/configuration/ResourceLimitVO.java +++ b/engine/schema/src/main/java/com/cloud/configuration/ResourceLimitVO.java @@ -47,10 +47,13 @@ public class ResourceLimitVO implements ResourceLimit { @Column(name = "max") private Long max; + @Column(name = "tag") + private String tag; + public ResourceLimitVO() { } - public ResourceLimitVO(ResourceCount.ResourceType type, Long max, long ownerId, ResourceOwnerType ownerType) { + public ResourceLimitVO(ResourceCount.ResourceType type, Long max, long ownerId, ResourceOwnerType ownerType, String tag) { this.type = type; this.max = max; @@ -59,6 +62,11 @@ public ResourceLimitVO(ResourceCount.ResourceType type, Long max, long ownerId, } else if (ownerType == ResourceOwnerType.Domain) { this.domainId = ownerId; } + this.tag = tag; + } + + public ResourceLimitVO(ResourceCount.ResourceType type, Long max, long ownerId, ResourceOwnerType ownerType) { + this(type, max, ownerId, ownerType, null); } @Override @@ -123,4 +131,12 @@ public void setAccountId(Long accountId) { this.accountId = accountId; } + @Override + public String getTag() { + return tag; + } + + public void setTag(String tag) { + this.tag = tag; + } } diff --git a/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceCountDao.java b/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceCountDao.java index 28f2a5360716..ebacbd8b3e5a 100644 --- a/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceCountDao.java +++ b/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceCountDao.java @@ -26,18 +26,20 @@ public interface ResourceCountDao extends GenericDao { /** - * @param domainId the id of the domain to get the resource count + * @param ownerId the id of the owner to get the resource count * @param type the type of resource (e.g. user_vm, public_ip, volume) + * @param tag for the type of resource * @return the count of resources in use for the given type and domain */ - long getResourceCount(long ownerId, ResourceOwnerType ownerType, ResourceType type); + long getResourceCount(long ownerId, ResourceOwnerType ownerType, ResourceType type, String tag); /** - * @param domainId the id of the domain to set the resource count + * @param ownerId the id of the owner to set the resource count * @param type the type of resource (e.g. user_vm, public_ip, volume) - * @param the count of resources in use for the given type and domain + * @param tag the tag for the type of resource + * @param count the count of resources in use for the given type and domain */ - void setResourceCount(long ownerId, ResourceOwnerType ownerType, ResourceType type, long count); + void setResourceCount(long ownerId, ResourceOwnerType ownerType, ResourceType type, String tag, long count); boolean updateById(long id, boolean increment, long delta); @@ -45,27 +47,22 @@ public interface ResourceCountDao extends GenericDao { List listByOwnerId(long ownerId, ResourceOwnerType ownerType); - ResourceCountVO findByOwnerAndType(long ownerId, ResourceOwnerType ownerType, ResourceType type); + ResourceCountVO findByOwnerAndTypeAndTag(long ownerId, ResourceOwnerType ownerType, ResourceType type, String tag); + + List findByOwnersAndTypeAndTag(List ownerIdList, ResourceOwnerType ownerType, + ResourceType type, String tag); List listResourceCountByOwnerType(ResourceOwnerType ownerType); - Set listAllRowsToUpdate(long ownerId, ResourceOwnerType ownerType, ResourceType type); + Set listAllRowsToUpdate(long ownerId, ResourceOwnerType ownerType, ResourceType type, String tag); + + boolean updateCountByDeltaForIds(List ids, boolean increment, long delta); - Set listRowsToUpdateForDomain(long domainId, ResourceType type); + Set listRowsToUpdateForDomain(long domainId, ResourceType type, String tag); long removeEntriesByOwner(long ownerId, ResourceOwnerType ownerType); - /** - * Counts the number of CPU cores allocated for the given account. - * - * Side note: This method is not using the "resource_count" table. It is executing the actual count instead. - */ - long countCpuNumberAllocatedToAccount(long accountId); + void removeResourceCountsForNonMatchingTags(Long ownerId, ResourceOwnerType ownerType, List types, List tags); - /** - * Counts the amount of memory allocated for the given account. - * - * Side note: This method is not using the "resource_count" table. It is executing the actual count instead. - */ - long countMemoryAllocatedToAccount(long accountId); + List lockRows(Set ids); } diff --git a/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceCountDaoImpl.java b/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceCountDaoImpl.java index ca6f13d2d645..2083fb422d28 100644 --- a/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceCountDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceCountDaoImpl.java @@ -20,13 +20,18 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import javax.annotation.PostConstruct; import javax.inject.Inject; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import com.cloud.configuration.Resource; @@ -37,6 +42,7 @@ import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; import com.cloud.user.AccountVO; +import com.cloud.user.ResourceLimitService; import com.cloud.user.dao.AccountDao; import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; @@ -49,24 +55,49 @@ @Component public class ResourceCountDaoImpl extends GenericDaoBase implements ResourceCountDao { private final SearchBuilder TypeSearch; - + private final SearchBuilder TypeNullTagSearch; + private final SearchBuilder NonMatchingTagsSearch; private final SearchBuilder AccountSearch; private final SearchBuilder DomainSearch; + private final SearchBuilder IdsSearch; @Inject private DomainDao _domainDao; @Inject private AccountDao _accountDao; + protected static final String INCREMENT_COUNT_BY_IDS_SQL = "UPDATE `cloud`.`resource_count` SET `count` = `count` + ? WHERE `id` IN (?)"; + protected static final String DECREMENT_COUNT_BY_IDS_SQL = "UPDATE `cloud`.`resource_count` SET `count` = `count` - ? WHERE `id` IN (?)"; + public ResourceCountDaoImpl() { TypeSearch = createSearchBuilder(); TypeSearch.and("type", TypeSearch.entity().getType(), SearchCriteria.Op.EQ); - TypeSearch.and("accountId", TypeSearch.entity().getAccountId(), SearchCriteria.Op.EQ); - TypeSearch.and("domainId", TypeSearch.entity().getDomainId(), SearchCriteria.Op.EQ); + TypeSearch.and("accountId", TypeSearch.entity().getAccountId(), SearchCriteria.Op.IN); + TypeSearch.and("domainId", TypeSearch.entity().getDomainId(), SearchCriteria.Op.IN); + TypeSearch.and("tag", TypeSearch.entity().getTag(), SearchCriteria.Op.EQ); TypeSearch.done(); + TypeNullTagSearch = createSearchBuilder(); + TypeNullTagSearch.and("type", TypeNullTagSearch.entity().getType(), SearchCriteria.Op.EQ); + TypeNullTagSearch.and("accountId", TypeNullTagSearch.entity().getAccountId(), SearchCriteria.Op.IN); + TypeNullTagSearch.and("domainId", TypeNullTagSearch.entity().getDomainId(), SearchCriteria.Op.IN); + TypeNullTagSearch.and("tag", TypeNullTagSearch.entity().getTag(), SearchCriteria.Op.NULL); + TypeNullTagSearch.done(); + + NonMatchingTagsSearch = createSearchBuilder(); + NonMatchingTagsSearch.and("accountId", NonMatchingTagsSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + NonMatchingTagsSearch.and("domainId", NonMatchingTagsSearch.entity().getDomainId(), SearchCriteria.Op.EQ); + NonMatchingTagsSearch.and("types", NonMatchingTagsSearch.entity().getType(), SearchCriteria.Op.IN); + NonMatchingTagsSearch.and("tagNotNull", NonMatchingTagsSearch.entity().getTag(), SearchCriteria.Op.NNULL); + NonMatchingTagsSearch.and("tags", NonMatchingTagsSearch.entity().getTag(), SearchCriteria.Op.NIN); + NonMatchingTagsSearch.done(); + AccountSearch = createSearchBuilder(); DomainSearch = createSearchBuilder(); + + IdsSearch = createSearchBuilder(); + IdsSearch.and("id", IdsSearch.entity().getId(), SearchCriteria.Op.IN); + IdsSearch.done(); } @PostConstruct @@ -85,24 +116,40 @@ protected void configure() { } @Override - public ResourceCountVO findByOwnerAndType(long ownerId, ResourceOwnerType ownerType, ResourceType type) { - SearchCriteria sc = TypeSearch.create(); + public ResourceCountVO findByOwnerAndTypeAndTag(long ownerId, ResourceOwnerType ownerType, ResourceType type, String tag) { + List resourceCounts = findByOwnersAndTypeAndTag(List.of(ownerId), ownerType, type, tag); + if (CollectionUtils.isNotEmpty(resourceCounts)) { + return resourceCounts.get(0); + } else { + return null; + } + } + + @Override + public List findByOwnersAndTypeAndTag(List ownerIdList, ResourceOwnerType ownerType, ResourceType type, String tag) { + if (CollectionUtils.isEmpty(ownerIdList)) { + return new ArrayList<>(); + } + SearchCriteria sc = tag != null ? TypeSearch.create() : TypeNullTagSearch.create(); sc.setParameters("type", type); + if (tag != null) { + sc.setParameters("tag", tag); + } if (ownerType == ResourceOwnerType.Account) { - sc.setParameters("accountId", ownerId); - return findOneIncludingRemovedBy(sc); + sc.setParameters("accountId", ownerIdList.toArray()); + return listIncludingRemovedBy(sc); } else if (ownerType == ResourceOwnerType.Domain) { - sc.setParameters("domainId", ownerId); - return findOneIncludingRemovedBy(sc); + sc.setParameters("domainId", ownerIdList.toArray()); + return listIncludingRemovedBy(sc); } else { - return null; + return new ArrayList<>(); } } @Override - public long getResourceCount(long ownerId, ResourceOwnerType ownerType, ResourceType type) { - ResourceCountVO vo = findByOwnerAndType(ownerId, ownerType, type); + public long getResourceCount(long ownerId, ResourceOwnerType ownerType, ResourceType type, String tag) { + ResourceCountVO vo = findByOwnerAndTypeAndTag(ownerId, ownerType, type, tag); if (vo != null) { return vo.getCount(); } else { @@ -111,8 +158,8 @@ public long getResourceCount(long ownerId, ResourceOwnerType ownerType, Resource } @Override - public void setResourceCount(long ownerId, ResourceOwnerType ownerType, ResourceType type, long count) { - ResourceCountVO resourceCountVO = findByOwnerAndType(ownerId, ownerType, type); + public void setResourceCount(long ownerId, ResourceOwnerType ownerType, ResourceType type, String tag, long count) { + ResourceCountVO resourceCountVO = findByOwnerAndTypeAndTag(ownerId, ownerType, type, tag); if (resourceCountVO != null && count != resourceCountVO.getCount()) { resourceCountVO.setCount(count); update(resourceCountVO.getId(), resourceCountVO); @@ -129,38 +176,79 @@ public boolean updateById(long id, boolean increment, long delta) { } @Override - public Set listRowsToUpdateForDomain(long domainId, ResourceType type) { + public boolean updateCountByDeltaForIds(List ids, boolean increment, long delta) { + if (CollectionUtils.isEmpty(ids)) { + return false; + } + String updateSql = increment ? INCREMENT_COUNT_BY_IDS_SQL : DECREMENT_COUNT_BY_IDS_SQL; + + String poolIdsInStr = ids.stream().map(String::valueOf).collect(Collectors.joining(",", "(", ")")); + String sql = updateSql.replace("(?)", poolIdsInStr); + + final TransactionLegacy txn = TransactionLegacy.currentTxn(); + try(PreparedStatement pstmt = txn.prepareStatement(sql);) { + pstmt.setLong(1, delta); + pstmt.executeUpdate(); + return true; + } catch (SQLException e) { + throw new CloudRuntimeException(e); + } + } + + @Override + public Set listRowsToUpdateForDomain(long domainId, ResourceType type, String tag) { Set rowIds = new HashSet(); Set domainIdsToUpdate = _domainDao.getDomainParentIds(domainId); for (Long domainIdToUpdate : domainIdsToUpdate) { - ResourceCountVO domainCountRecord = findByOwnerAndType(domainIdToUpdate, ResourceOwnerType.Domain, type); + ResourceCountVO domainCountRecord = findByOwnerAndTypeAndTag(domainIdToUpdate, ResourceOwnerType.Domain, type, tag); if (domainCountRecord != null) { rowIds.add(domainCountRecord.getId()); + } else { + if (StringUtils.isNotEmpty(tag)) { + ResourceCountVO resourceCountVO = createTaggedResourceCount(domainIdToUpdate, ResourceOwnerType.Domain, type, tag); + rowIds.add(resourceCountVO.getId()); + } } } return rowIds; } @Override - public Set listAllRowsToUpdate(long ownerId, ResourceOwnerType ownerType, ResourceType type) { + public Set listAllRowsToUpdate(long ownerId, ResourceOwnerType ownerType, ResourceType type, String tag) { Set rowIds = new HashSet(); if (ownerType == ResourceOwnerType.Account) { //get records for account - ResourceCountVO accountCountRecord = findByOwnerAndType(ownerId, ResourceOwnerType.Account, type); + ResourceCountVO accountCountRecord = findByOwnerAndTypeAndTag(ownerId, ResourceOwnerType.Account, type, tag); if (accountCountRecord != null) { rowIds.add(accountCountRecord.getId()); + } else { + if (StringUtils.isNotEmpty(tag)) { + ResourceCountVO resourceCountVO = createTaggedResourceCount(ownerId, ownerType, type, tag); + rowIds.add(resourceCountVO.getId()); + } } //get records for account's domain and all its parent domains - rowIds.addAll(listRowsToUpdateForDomain(_accountDao.findByIdIncludingRemoved(ownerId).getDomainId(), type)); + rowIds.addAll(listRowsToUpdateForDomain(_accountDao.findByIdIncludingRemoved(ownerId).getDomainId(), type, tag)); } else if (ownerType == ResourceOwnerType.Domain) { - return listRowsToUpdateForDomain(ownerId, type); + rowIds = listRowsToUpdateForDomain(ownerId, type, tag); } return rowIds; } + protected ResourceCountVO createTaggedResourceCount(long ownerId, ResourceLimit.ResourceOwnerType ownerType, ResourceType resourceType, String tag) { + ResourceCountVO taggedResourceCountVO = new ResourceCountVO(resourceType, 0, ownerId, ownerType, tag); + return persist(taggedResourceCountVO); + } + + protected void createTaggedResourceCounts(long ownerId, ResourceLimit.ResourceOwnerType ownerType, ResourceType resourceType, List tags) { + for (String tag : tags) { + createTaggedResourceCount(ownerId, ownerType, resourceType, tag); + } + } + @Override @DB public void createResourceCounts(long ownerId, ResourceLimit.ResourceOwnerType ownerType) { @@ -169,9 +257,23 @@ public void createResourceCounts(long ownerId, ResourceLimit.ResourceOwnerType o txn.start(); ResourceType[] resourceTypes = Resource.ResourceType.values(); + List hostTags = new ArrayList<>(); + if (StringUtils.isNotEmpty(ResourceLimitService.ResourceLimitHostTags.value())) { + hostTags = Arrays.asList(ResourceLimitService.ResourceLimitHostTags.value().split(",")); + } + List storageTags = new ArrayList<>(); + if (StringUtils.isNotEmpty(ResourceLimitService.ResourceLimitStorageTags.value())) { + storageTags = Arrays.asList(ResourceLimitService.ResourceLimitStorageTags.value().split(",")); + } for (ResourceType resourceType : resourceTypes) { ResourceCountVO resourceCountVO = new ResourceCountVO(resourceType, 0, ownerId, ownerType); persist(resourceCountVO); + if (ResourceLimitService.HostTagsSupportingTypes.contains(resourceType)) { + createTaggedResourceCounts(ownerId, ownerType, resourceType, hostTags); + } + if (ResourceLimitService.StorageTagsSupportingTypes.contains(resourceType)) { + createTaggedResourceCounts(ownerId, ownerType, resourceType, storageTags); + } } txn.commit(); @@ -234,22 +336,9 @@ public long removeEntriesByOwner(long ownerId, ResourceOwnerType ownerType) { + " END)) as total " + " from vm_instance vm " + " join service_offering so on so.id = vm.service_offering_id " - + " left join user_vm_details vmd on vmd.vm_id = vm.id and vmd.name = '%s' " + + " left join vm_instance_details vmd on vmd.vm_id = vm.id and vmd.name = '%s' " + " where vm.type = 'User' and state not in ('Destroyed', 'Error', 'Expunging') and display_vm = true and account_id = ? "; - @Override - public long countCpuNumberAllocatedToAccount(long accountId) { - String sqlCountCpuNumberAllocatedToAccount = String.format(baseSqlCountComputingResourceAllocatedToAccount, ResourceType.cpu, ResourceType.cpu, "cpuNumber"); - return executeSqlCountComputingResourcesForAccount(accountId, sqlCountCpuNumberAllocatedToAccount); - } - - @Override - public long countMemoryAllocatedToAccount(long accountId) { - String serviceOfferingRamSizeField = "ram_size"; - String sqlCountCpuNumberAllocatedToAccount = String.format(baseSqlCountComputingResourceAllocatedToAccount, serviceOfferingRamSizeField, serviceOfferingRamSizeField, "memory"); - return executeSqlCountComputingResourcesForAccount(accountId, sqlCountCpuNumberAllocatedToAccount); - } - private long executeSqlCountComputingResourcesForAccount(long accountId, String sqlCountComputingResourcesAllocatedToAccount) { TransactionLegacy tx = TransactionLegacy.currentTxn(); try { @@ -266,4 +355,32 @@ private long executeSqlCountComputingResourcesForAccount(long accountId, String } } + @Override + public void removeResourceCountsForNonMatchingTags(Long ownerId, ResourceOwnerType ownerType, List types, List tags) { + SearchCriteria sc = NonMatchingTagsSearch.create(); + if (ObjectUtils.allNotNull(ownerId, ownerType)) { + if (ResourceOwnerType.Account.equals(ownerType)) { + sc.setParameters("accountId", ownerId); + } else { + sc.setParameters("domainId", ownerId); + } + } + if (CollectionUtils.isNotEmpty(types)) { + sc.setParameters("types", types.stream().map(ResourceType::getName).toArray()); + } + if (CollectionUtils.isNotEmpty(tags)) { + sc.setParameters("tags", tags.toArray()); + } + remove(sc); + } + + @Override + public List lockRows(Set ids) { + if (CollectionUtils.isEmpty(ids)) { + return new ArrayList<>(); + } + SearchCriteria sc = IdsSearch.create(); + sc.setParameters("id", ids.toArray()); + return lockRows(sc, null, true); + } } diff --git a/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceLimitDao.java b/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceLimitDao.java index e47b38340c2b..7cdc2aacc3ba 100644 --- a/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceLimitDao.java +++ b/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceLimitDao.java @@ -18,6 +18,7 @@ import java.util.List; +import com.cloud.configuration.Resource; import com.cloud.configuration.Resource.ResourceOwnerType; import com.cloud.configuration.ResourceCount; import com.cloud.configuration.ResourceLimitVO; @@ -31,7 +32,8 @@ public interface ResourceLimitDao extends GenericDao { ResourceCount.ResourceType getLimitType(String type); - ResourceLimitVO findByOwnerIdAndType(long ownerId, ResourceOwnerType ownerType, ResourceCount.ResourceType type); + ResourceLimitVO findByOwnerIdAndTypeAndTag(long ownerId, ResourceOwnerType ownerType, ResourceCount.ResourceType type, String tag); long removeEntriesByOwner(Long ownerId, ResourceOwnerType ownerType); + void removeResourceLimitsForNonMatchingTags(Long ownerId, ResourceOwnerType ownerType, List types, List tags); } diff --git a/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceLimitDaoImpl.java b/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceLimitDaoImpl.java index 03c2d2a46247..96523ba9bea3 100644 --- a/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceLimitDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceLimitDaoImpl.java @@ -20,6 +20,8 @@ import java.util.List; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; import org.springframework.stereotype.Component; import com.cloud.configuration.Resource; @@ -33,19 +35,36 @@ @Component public class ResourceLimitDaoImpl extends GenericDaoBase implements ResourceLimitDao { - private SearchBuilder IdTypeSearch; + private SearchBuilder IdTypeTagSearch; + private SearchBuilder IdTypeNullTagSearch; + private SearchBuilder NonMatchingTagsSearch; public ResourceLimitDaoImpl() { - IdTypeSearch = createSearchBuilder(); - IdTypeSearch.and("type", IdTypeSearch.entity().getType(), SearchCriteria.Op.EQ); - IdTypeSearch.and("domainId", IdTypeSearch.entity().getDomainId(), SearchCriteria.Op.EQ); - IdTypeSearch.and("accountId", IdTypeSearch.entity().getAccountId(), SearchCriteria.Op.EQ); - IdTypeSearch.done(); + IdTypeTagSearch = createSearchBuilder(); + IdTypeTagSearch.and("type", IdTypeTagSearch.entity().getType(), SearchCriteria.Op.EQ); + IdTypeTagSearch.and("domainId", IdTypeTagSearch.entity().getDomainId(), SearchCriteria.Op.EQ); + IdTypeTagSearch.and("accountId", IdTypeTagSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + IdTypeTagSearch.and("tag", IdTypeTagSearch.entity().getTag(), SearchCriteria.Op.EQ); + + IdTypeNullTagSearch = createSearchBuilder(); + IdTypeNullTagSearch.and("type", IdTypeNullTagSearch.entity().getType(), SearchCriteria.Op.EQ); + IdTypeNullTagSearch.and("domainId", IdTypeNullTagSearch.entity().getDomainId(), SearchCriteria.Op.EQ); + IdTypeNullTagSearch.and("accountId", IdTypeNullTagSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + IdTypeNullTagSearch.and("tag", IdTypeNullTagSearch.entity().getTag(), SearchCriteria.Op.NULL); + IdTypeNullTagSearch.done(); + + NonMatchingTagsSearch = createSearchBuilder(); + NonMatchingTagsSearch.and("accountId", NonMatchingTagsSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + NonMatchingTagsSearch.and("domainId", NonMatchingTagsSearch.entity().getDomainId(), SearchCriteria.Op.EQ); + NonMatchingTagsSearch.and("types", NonMatchingTagsSearch.entity().getType(), SearchCriteria.Op.IN); + NonMatchingTagsSearch.and("tagNotNull", NonMatchingTagsSearch.entity().getTag(), SearchCriteria.Op.NNULL); + NonMatchingTagsSearch.and("tags", NonMatchingTagsSearch.entity().getTag(), SearchCriteria.Op.NIN); + NonMatchingTagsSearch.done(); } @Override public List listByOwner(Long ownerId, ResourceOwnerType ownerType) { - SearchCriteria sc = IdTypeSearch.create(); + SearchCriteria sc = IdTypeTagSearch.create(); if (ownerType == ResourceOwnerType.Account) { sc.setParameters("accountId", ownerId); @@ -81,9 +100,12 @@ public ResourceCount.ResourceType getLimitType(String type) { } @Override - public ResourceLimitVO findByOwnerIdAndType(long ownerId, ResourceOwnerType ownerType, ResourceCount.ResourceType type) { - SearchCriteria sc = IdTypeSearch.create(); + public ResourceLimitVO findByOwnerIdAndTypeAndTag(long ownerId, ResourceOwnerType ownerType, ResourceCount.ResourceType type, String tag) { + SearchCriteria sc = tag != null ? IdTypeTagSearch.create() : IdTypeNullTagSearch.create(); sc.setParameters("type", type); + if (tag != null) { + sc.setParameters("tag", tag); + } if (ownerType == ResourceOwnerType.Account) { sc.setParameters("accountId", ownerId); @@ -98,7 +120,7 @@ public ResourceLimitVO findByOwnerIdAndType(long ownerId, ResourceOwnerType owne @Override public long removeEntriesByOwner(Long ownerId, ResourceOwnerType ownerType) { - SearchCriteria sc = IdTypeSearch.create(); + SearchCriteria sc = IdTypeTagSearch.create(); if (ownerType == ResourceOwnerType.Account) { sc.setParameters("accountId", ownerId); @@ -109,4 +131,23 @@ public long removeEntriesByOwner(Long ownerId, ResourceOwnerType ownerType) { } return 0; } + + @Override + public void removeResourceLimitsForNonMatchingTags(Long ownerId, ResourceOwnerType ownerType, List types, List tags) { + SearchCriteria sc = NonMatchingTagsSearch.create(); + if (ObjectUtils.allNotNull(ownerId, ownerType)) { + if (ResourceOwnerType.Account.equals(ownerType)) { + sc.setParameters("accountId", ownerId); + } else { + sc.setParameters("domainId", ownerId); + } + } + if (CollectionUtils.isNotEmpty(types)) { + sc.setParameters("types", types.stream().map(ResourceType::getName).toArray()); + } + if (CollectionUtils.isNotEmpty(tags)) { + sc.setParameters("tags", tags.toArray()); + } + remove(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/dc/ASNumberRangeVO.java b/engine/schema/src/main/java/com/cloud/dc/ASNumberRangeVO.java new file mode 100644 index 000000000000..3790213b3ade --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/dc/ASNumberRangeVO.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 com.cloud.dc; + +import com.cloud.bgp.ASNumberRange; +import com.cloud.utils.db.GenericDao; + +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.GregorianCalendar; +import java.util.UUID; + +@Entity +@Table(name = "as_number_range") +public class ASNumberRangeVO implements ASNumberRange { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "data_center_id") + private long dataCenterId; + + @Column(name = "start_as_number") + private long startASNumber; + + @Column(name = "end_as_number") + private long endASNumber; + + @Column(name = GenericDao.REMOVED_COLUMN) + private Date removed; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + public ASNumberRangeVO() { + this.uuid = UUID.randomUUID().toString(); + this.created = GregorianCalendar.getInstance().getTime(); + } + + public ASNumberRangeVO(long dataCenterId, long startASNumber, long endASNumber) { + this(); + this.dataCenterId = dataCenterId; + this.startASNumber = startASNumber; + this.endASNumber = endASNumber; + } + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public long getDataCenterId() { + return dataCenterId; + } + + @Override + public long getStartASNumber() { + return startASNumber; + } + + @Override + public long getEndASNumber() { + return endASNumber; + } + + public Date getRemoved() { + return removed; + } + + @Override + public Date getCreated() { + return created; + } +} diff --git a/engine/schema/src/main/java/com/cloud/dc/ASNumberVO.java b/engine/schema/src/main/java/com/cloud/dc/ASNumberVO.java new file mode 100644 index 000000000000..529d1cfb5fe8 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/dc/ASNumberVO.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 com.cloud.dc; + +import com.cloud.bgp.ASNumber; +import com.cloud.utils.db.GenericDao; + +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 = "as_number") +public class ASNumberVO implements ASNumber { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "account_id") + private Long accountId; + + @Column(name = "domain_id") + private Long domainId; + + @Column(name = "as_number") + private long asNumber; + + @Column(name = "as_number_range_id") + private long asNumberRangeId; + + @Column(name = "data_center_id") + private long dataCenterId; + + @Column(name = "allocated") + @Temporal(value = TemporalType.TIMESTAMP) + private Date allocatedTime; + + @Column(name = "is_allocated") + private boolean allocated; + + @Column(name = "network_id") + private Long networkId; + + @Column(name = "vpc_id") + private Long vpcId; + + @Column(name = GenericDao.REMOVED_COLUMN) + private Date removed; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + public ASNumberVO() { + this.uuid = UUID.randomUUID().toString(); + this.created = new Date(); + } + + public ASNumberVO(long asNumber, long asNumberRangeId, long dataCenterId) { + this(); + this.asNumber = asNumber; + this.asNumberRangeId = asNumberRangeId; + this.dataCenterId = dataCenterId; + } + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + public void setAccountId(Long accountId) { + this.accountId = accountId; + } + + @Override + public Long getAccountId() { + return accountId; + } + + public void setDomainId(Long domainId) { + this.domainId = domainId; + } + + @Override + public Long getDomainId() { + return domainId; + } + + @Override + public long getAsNumber() { + return asNumber; + } + + @Override + public long getAsNumberRangeId() { + return asNumberRangeId; + } + + @Override + public long getDataCenterId() { + return dataCenterId; + } + + public void setAllocatedTime(Date date) { + this.allocatedTime = date; + } + + @Override + public Date getAllocatedTime() { + return allocatedTime; + } + + public void setAllocated(boolean allocated) { + this.allocated = allocated; + } + + @Override + public boolean isAllocated() { + return allocated; + } + + public void setNetworkId(Long networkId) { + this.networkId = networkId; + } + + @Override + public Long getNetworkId() { + return networkId; + } + + @Override + public Date getRemoved() { + return removed; + } + + @Override + public Date getCreated() { + return created; + } + + public Long getVpcId() { + return vpcId; + } + + public void setVpcId(Long vpcId) { + this.vpcId = vpcId; + } +} diff --git a/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsDao.java b/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsDao.java index 06c9c5255048..27cea8d5c2d1 100644 --- a/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsDao.java +++ b/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsDao.java @@ -16,11 +16,13 @@ // under the License. package com.cloud.dc; +import java.util.Collection; import java.util.Map; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; -public interface ClusterDetailsDao extends GenericDao { +public interface ClusterDetailsDao extends GenericDao, ResourceDetailsDao { Map findDetails(long clusterId); void persist(long clusterId, Map details); @@ -29,6 +31,8 @@ public interface ClusterDetailsDao extends GenericDao { ClusterDetailsVO findDetail(long clusterId, String name); + Map findDetails(long clusterId, Collection names); + void deleteDetails(long clusterId); String getVmwareDcName(Long clusterId); 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 c2058ad5644b..a8888f98ad29 100644 --- a/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsDaoImpl.java @@ -16,22 +16,33 @@ // under the License. package com.cloud.dc; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; +import javax.inject.Inject; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.ConfigKey.Scope; import org.apache.cloudstack.framework.config.ScopedConfigStorage; +import org.apache.commons.collections.CollectionUtils; +import com.cloud.dc.dao.ClusterDao; +import com.cloud.org.Cluster; +import com.cloud.utils.Pair; import com.cloud.utils.crypt.DBEncryptionUtil; -import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.TransactionLegacy; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; + +public class ClusterDetailsDaoImpl extends ResourceDetailsDaoBase implements ClusterDetailsDao, ScopedConfigStorage { + + @Inject + ClusterDao clusterDao; -public class ClusterDetailsDaoImpl extends GenericDaoBase implements ClusterDetailsDao, ScopedConfigStorage { protected final SearchBuilder ClusterSearch; protected final SearchBuilder DetailSearch; @@ -42,11 +53,11 @@ public class ClusterDetailsDaoImpl extends GenericDaoBase findDetails(long clusterId) { SearchCriteria sc = ClusterSearch.create(); @@ -83,6 +99,23 @@ public Map findDetails(long clusterId) { return details; } + @Override + public Map findDetails(long clusterId, Collection names) { + if (CollectionUtils.isEmpty(names)) { + return new HashMap<>(); + } + SearchBuilder sb = createSearchBuilder(); + sb.and("clusterId", sb.entity().getResourceId(), SearchCriteria.Op.EQ); + sb.and("name", sb.entity().getName(), SearchCriteria.Op.IN); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("clusterId", clusterId); + sc.setParameters("name", names.toArray()); + List results = search(sc, null); + return results.stream() + .collect(Collectors.toMap(ClusterDetailsVO::getName, ClusterDetailsVO::getValue)); + } + @Override public void deleteDetails(long clusterId) { SearchCriteria sc = ClusterSearch.create(); @@ -136,9 +169,9 @@ public Scope getScope() { } @Override - public String getConfigValue(long id, ConfigKey key) { - ClusterDetailsVO vo = findDetail(id, key.key()); - return vo == null ? null : vo.getValue(); + public String getConfigValue(long id, String key) { + ClusterDetailsVO vo = findDetail(id, key); + return vo == null ? null : getActualValue(vo); } @Override @@ -161,4 +194,13 @@ private String getCpuMemoryOvercommitRatio(String name) { return name; } + + @Override + public Pair getParentScope(long id) { + Cluster cluster = clusterDao.findById(id); + if (cluster == null) { + return null; + } + return new Pair<>(getScope().getParent(), cluster.getDataCenterId()); + } } diff --git a/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsVO.java b/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsVO.java index 6eb9e7466a7e..b213f8f2594b 100644 --- a/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsVO.java +++ b/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsVO.java @@ -23,11 +23,11 @@ import javax.persistence.Id; import javax.persistence.Table; -import org.apache.cloudstack.api.InternalIdentity; +import org.apache.cloudstack.api.ResourceDetail; @Entity @Table(name = "cluster_details") -public class ClusterDetailsVO implements InternalIdentity { +public class ClusterDetailsVO implements ResourceDetail { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -35,7 +35,7 @@ public class ClusterDetailsVO implements InternalIdentity { private long id; @Column(name = "cluster_id") - private long clusterId; + private long resourceId; @Column(name = "name") private String name; @@ -47,13 +47,14 @@ protected ClusterDetailsVO() { } public ClusterDetailsVO(long clusterId, String name, String value) { - this.clusterId = clusterId; + this.resourceId = clusterId; this.name = name; this.value = value; } - public long getClusterId() { - return clusterId; + @Override + public long getResourceId() { + return resourceId; } public String getName() { @@ -64,6 +65,11 @@ 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/com/cloud/dc/ClusterVO.java b/engine/schema/src/main/java/com/cloud/dc/ClusterVO.java index f60765e28566..a18097db6d6c 100644 --- a/engine/schema/src/main/java/com/cloud/dc/ClusterVO.java +++ b/engine/schema/src/main/java/com/cloud/dc/ClusterVO.java @@ -16,14 +16,18 @@ // under the License. package com.cloud.dc; +import com.cloud.cpu.CPU; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.org.Cluster; import com.cloud.org.Grouping; import com.cloud.org.Managed.ManagedState; import com.cloud.utils.NumbersUtil; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.util.CPUArchConverter; +import org.apache.cloudstack.util.HypervisorTypeConverter; import javax.persistence.Column; +import javax.persistence.Convert; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; @@ -56,6 +60,7 @@ public class ClusterVO implements Cluster { long podId; @Column(name = "hypervisor_type") + @Convert(converter = HypervisorTypeConverter.class) String hypervisorType; @Column(name = "cluster_type") @@ -66,6 +71,10 @@ public class ClusterVO implements Cluster { @Enumerated(value = EnumType.STRING) AllocationState allocationState; + @Column(name = "arch") + @Convert(converter = CPUArchConverter.class) + private String arch; + @Column(name = "managed_state") @Enumerated(value = EnumType.STRING) ManagedState managedState; @@ -76,6 +85,10 @@ public class ClusterVO implements Cluster { @Column(name = "uuid") String uuid; + @Column(name = "storage_access_groups") + String storageAccessGroups; + + public ClusterVO() { clusterType = Cluster.ClusterType.CloudManaged; allocationState = Grouping.AllocationState.Enabled; @@ -197,6 +210,23 @@ public PartitionType partitionType() { return PartitionType.Cluster; } + @Override + public CPU.CPUArch getArch() { + return CPU.CPUArch.fromType(arch); + } + + public void setArch(String arch) { + this.arch = arch; + } + + public String getStorageAccessGroups() { + return storageAccessGroups; + } + + public void setStorageAccessGroups(String storageAccessGroups) { + this.storageAccessGroups = storageAccessGroups; + } + @Override public String toString() { return String.format("Cluster {id: \"%s\", name: \"%s\", uuid: \"%s\"}", id, name, uuid); diff --git a/engine/schema/src/main/java/com/cloud/dc/DataCenterIpAddressVO.java b/engine/schema/src/main/java/com/cloud/dc/DataCenterIpAddressVO.java index 3d68cc3d9a86..874b05673eb1 100644 --- a/engine/schema/src/main/java/com/cloud/dc/DataCenterIpAddressVO.java +++ b/engine/schema/src/main/java/com/cloud/dc/DataCenterIpAddressVO.java @@ -55,7 +55,7 @@ public class DataCenterIpAddressVO implements InternalIdentity { String reservationId; @Column(name = "nic_id") - private Long instanceId; + private Long nicId; @Column(name = "mac_address") long macAddress; @@ -88,12 +88,12 @@ public long getId() { return id; } - public Long getInstanceId() { - return instanceId; + public Long getNicId() { + return nicId; } - public void setInstanceId(Long instanceId) { - this.instanceId = instanceId; + public void setNicId(Long nicId) { + this.nicId = nicId; } public long getPodId() { diff --git a/engine/schema/src/main/java/com/cloud/dc/DataCenterVO.java b/engine/schema/src/main/java/com/cloud/dc/DataCenterVO.java index 827b72b58b05..9b24e51a1a8b 100644 --- a/engine/schema/src/main/java/com/cloud/dc/DataCenterVO.java +++ b/engine/schema/src/main/java/com/cloud/dc/DataCenterVO.java @@ -142,6 +142,9 @@ public class DataCenterVO implements DataCenter { @Enumerated(value = EnumType.STRING) private DataCenter.Type type; + @Column(name = "storage_access_groups") + String storageAccessGroups; + @Override public String getDnsProvider() { return dnsProvider; @@ -485,6 +488,14 @@ public void setType(Type type) { this.type = type; } + public String getStorageAccessGroups() { + return storageAccessGroups; + } + + public void setStorageAccessGroups(String storageAccessGroups) { + this.storageAccessGroups = storageAccessGroups; + } + @Override public String toString() { return String.format("Zone {\"id\": \"%s\", \"name\": \"%s\", \"uuid\": \"%s\"}", id, name, uuid); diff --git a/engine/schema/src/main/java/com/cloud/dc/HostPodVO.java b/engine/schema/src/main/java/com/cloud/dc/HostPodVO.java index d9971815f5e6..99ebcf2346c5 100644 --- a/engine/schema/src/main/java/com/cloud/dc/HostPodVO.java +++ b/engine/schema/src/main/java/com/cloud/dc/HostPodVO.java @@ -31,6 +31,7 @@ import com.cloud.org.Grouping; import com.cloud.utils.NumbersUtil; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "host_pod_ref") @@ -70,6 +71,9 @@ public class HostPodVO implements Pod { @Column(name = "uuid") private String uuid; + @Column(name = "storage_access_groups") + String storageAccessGroups; + public HostPodVO(String name, long dcId, String gateway, String cidrAddress, int cidrSize, String description) { this.name = name; this.dataCenterId = dcId; @@ -197,4 +201,19 @@ public String getUuid() { public void setUuid(String uuid) { this.uuid = uuid; } + + public String getStorageAccessGroups() { + return storageAccessGroups; + } + + public void setStorageAccessGroups(String storageAccessGroups) { + this.storageAccessGroups = storageAccessGroups; + } + + @Override + public String toString() { + return String.format("HostPod %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name")); + } } diff --git a/engine/schema/src/main/java/com/cloud/dc/VlanVO.java b/engine/schema/src/main/java/com/cloud/dc/VlanVO.java index 7423ded598f3..c271325f3dee 100644 --- a/engine/schema/src/main/java/com/cloud/dc/VlanVO.java +++ b/engine/schema/src/main/java/com/cloud/dc/VlanVO.java @@ -29,6 +29,7 @@ import javax.persistence.Table; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "vlan") @@ -192,24 +193,11 @@ public void setPhysicalNetworkId(Long physicalNetworkId) { @Override public String toString() { if (toString == null) { - toString = - new StringBuilder("Vlan[").append(vlanTag) - .append("|") - .append(vlanGateway) - .append("|") - .append(vlanNetmask) - .append("|") - .append(ip6Gateway) - .append("|") - .append(ip6Cidr) - .append("|") - .append(ipRange) - .append("|") - .append(ip6Range) - .append("|") - .append(networkId) - .append("]") - .toString(); + toString = String.format("Vlan %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "uuid", + "vlanTag", "vlanGateway", "vlanNetmask", "ip6Gateway", "ip6Cidr", + "ipRange", "ip6Range", "networkId")); + } return toString; } diff --git a/engine/schema/src/main/java/com/cloud/dc/VmwareDatacenterVO.java b/engine/schema/src/main/java/com/cloud/dc/VmwareDatacenterVO.java index 6390d923ed83..5a4a71f82e7f 100644 --- a/engine/schema/src/main/java/com/cloud/dc/VmwareDatacenterVO.java +++ b/engine/schema/src/main/java/com/cloud/dc/VmwareDatacenterVO.java @@ -28,6 +28,7 @@ import com.cloud.utils.NumbersUtil; import com.cloud.utils.db.Encrypt; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; /** * VmwareDatacenterVO contains information of Vmware Datacenter associated with a CloudStack zone. @@ -125,7 +126,9 @@ public void setPassword(String password) { @Override public String toString() { - return new StringBuilder("VmwareDatacenter[").append(guid).append("]").toString(); + return String.format("VmwareDatacenter %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "guid")); } @Override diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/ASNumberDao.java b/engine/schema/src/main/java/com/cloud/dc/dao/ASNumberDao.java new file mode 100644 index 000000000000..192f6bbaf315 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/dc/dao/ASNumberDao.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.dc.dao; + +import com.cloud.dc.ASNumberVO; +import com.cloud.user.Account; +import com.cloud.utils.Pair; +import com.cloud.utils.db.GenericDao; + +import java.util.List; + +public interface ASNumberDao extends GenericDao { + + Pair, Integer> searchAndCountByZoneOrRangeOrAllocated(Long zoneId, Long asnRangeId, Integer asNumber, Long networkId, Long vpcId, + Boolean allocated, Long accountId, Long domainId, String keyword, Account caller, + Long startIndex, Long pageSizeVal); + ASNumberVO findByAsNumber(Long asNumber); + + ASNumberVO findOneByAllocationStateAndZone(long zoneId, boolean allocated); + + List listAllocatedByASRange(Long asRangeId); + + ASNumberVO findByZoneAndNetworkId(long zoneId, long networkId); + ASNumberVO findByZoneAndVpcId(long zoneId, long vpcId); + + int removeASRangeNumbers(long rangeId); +} diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/ASNumberDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/ASNumberDaoImpl.java new file mode 100644 index 000000000000..1d2adf4d424d --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/dc/dao/ASNumberDaoImpl.java @@ -0,0 +1,141 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.dc.dao; + +import com.cloud.dc.ASNumberVO; +import com.cloud.user.Account; +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.Arrays; +import java.util.List; + +public class ASNumberDaoImpl extends GenericDaoBase implements ASNumberDao { + + private final SearchBuilder asNumberSearch; + + public ASNumberDaoImpl() { + asNumberSearch = createSearchBuilder(); + asNumberSearch.and("zoneId", asNumberSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + asNumberSearch.and("rangeId", asNumberSearch.entity().getAsNumberRangeId(), SearchCriteria.Op.EQ); + asNumberSearch.and("isAllocated", asNumberSearch.entity().isAllocated(), SearchCriteria.Op.EQ); + asNumberSearch.and("asNumber", asNumberSearch.entity().getAsNumber(), SearchCriteria.Op.EQ); + asNumberSearch.and("networkId", asNumberSearch.entity().getNetworkId(), SearchCriteria.Op.EQ); + asNumberSearch.and("vpcId", asNumberSearch.entity().getVpcId(), SearchCriteria.Op.EQ); + asNumberSearch.and("accountId", asNumberSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + asNumberSearch.and("domainId", asNumberSearch.entity().getDomainId(), SearchCriteria.Op.EQ); + asNumberSearch.done(); + } + + @Override + public Pair, Integer> searchAndCountByZoneOrRangeOrAllocated(Long zoneId, Long asnRangeId, + Integer asNumber, Long networkId, Long vpcId, + Boolean allocated, + Long accountId, Long domainId, + String keyword, Account caller, + Long startIndex, Long pageSizeVal) { + SearchCriteria sc = asNumberSearch.create(); + if (zoneId != null) { + sc.setParameters("zoneId", zoneId); + } + if (asnRangeId != null) { + sc.setParameters("rangeId", asnRangeId); + } + if (networkId != null) { + sc.setParameters("networkId", networkId); + } + if (vpcId != null) { + sc.setParameters("vpcId", vpcId); + } + if (allocated != null) { + sc.setParameters("isAllocated", allocated); + } + if (asNumber != null) { + sc.setParameters("asNumber", asNumber); + } + if (accountId != null) { + sc.setParameters("accountId", accountId); + } + if (domainId != null) { + sc.setParameters("domainId", domainId); + } + if (keyword != null) { + sc.addAnd("asNumber", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + } + if (Arrays.asList(Account.Type.DOMAIN_ADMIN, Account.Type.RESOURCE_DOMAIN_ADMIN).contains(caller.getType())) { + SearchCriteria scc = asNumberSearch.create(); + scc.addOr("domainId", SearchCriteria.Op.NULL); + scc.addOr("domainId", SearchCriteria.Op.EQ, caller.getDomainId()); + sc.addAnd("domainId", SearchCriteria.Op.SC, scc); + } else if (Arrays.asList(Account.Type.NORMAL, Account.Type.PROJECT).contains(caller.getType())) { + SearchCriteria scc = asNumberSearch.create(); + scc.addOr("domainId", SearchCriteria.Op.NULL); + scc.addOr("accountId", SearchCriteria.Op.EQ, caller.getAccountId()); + sc.addAnd("domainId", SearchCriteria.Op.SC, scc); + } + Filter searchFilter = new Filter(ASNumberVO.class, "id", true, startIndex, pageSizeVal); + return searchAndCount(sc, searchFilter); + } + + @Override + public ASNumberVO findByAsNumber(Long asNumber) { + SearchCriteria sc = asNumberSearch.create(); + sc.setParameters("asNumber", asNumber); + return findOneBy(sc); + } + + @Override + public ASNumberVO findOneByAllocationStateAndZone(long zoneId, boolean allocated) { + SearchCriteria sc = asNumberSearch.create(); + sc.setParameters("zoneId", zoneId); + sc.setParameters("isAllocated", allocated); + return findOneBy(sc); + } + + @Override + public List listAllocatedByASRange(Long asRangeId) { + SearchCriteria sc = asNumberSearch.create(); + sc.setParameters("rangeId", asRangeId); + sc.setParameters("isAllocated", true); + return listBy(sc); + } + + public ASNumberVO findByZoneAndNetworkId(long zoneId, long networkId) { + SearchCriteria sc = asNumberSearch.create(); + sc.setParameters("zoneId", zoneId); + sc.setParameters("networkId", networkId); + return findOneBy(sc); + } + + @Override + public ASNumberVO findByZoneAndVpcId(long zoneId, long vpcId) { + SearchCriteria sc = asNumberSearch.create(); + sc.setParameters("zoneId", zoneId); + sc.setParameters("vpcId", vpcId); + return findOneBy(sc); + } + + @Override + public int removeASRangeNumbers(long rangeId) { + SearchCriteria sc = asNumberSearch.create(); + sc.setParameters("rangeId", rangeId); + return remove(sc); + } +} diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/ASNumberRangeDao.java b/engine/schema/src/main/java/com/cloud/dc/dao/ASNumberRangeDao.java new file mode 100644 index 000000000000..3309a6f5fe54 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/dc/dao/ASNumberRangeDao.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.dc.dao; + +import com.cloud.dc.ASNumberRangeVO; +import com.cloud.utils.db.GenericDao; + +import java.util.List; + +public interface ASNumberRangeDao extends GenericDao { + + List listByZoneId(long zoneId); +} diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/ASNumberRangeDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/ASNumberRangeDaoImpl.java new file mode 100644 index 000000000000..4a4170685dc4 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/dc/dao/ASNumberRangeDaoImpl.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.dc.dao; + +import com.cloud.dc.ASNumberRangeVO; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +import java.util.List; + +public class ASNumberRangeDaoImpl extends GenericDaoBase implements ASNumberRangeDao { + + private final SearchBuilder searchBuilder; + + public ASNumberRangeDaoImpl() { + searchBuilder = createSearchBuilder(); + searchBuilder.and("zoneId", searchBuilder.entity().getDataCenterId(), SearchCriteria.Op.EQ); + searchBuilder.done(); + } + + @Override + public List listByZoneId(long zoneId) { + SearchCriteria sc = searchBuilder.create(); + sc.setParameters("zoneId", zoneId); + return listBy(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 ab9c5cab8c4a..6cfd2608f5de 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 @@ -16,26 +16,25 @@ // under the License. package com.cloud.dc.dao; +import java.util.List; +import java.util.Map; + +import com.cloud.cpu.CPU; import com.cloud.dc.ClusterVO; import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.utils.Pair; import com.cloud.utils.db.GenericDao; -import java.util.List; -import java.util.Map; -import java.util.Set; - public interface ClusterDao extends GenericDao { List listByPodId(long podId); ClusterVO findBy(String name, long podId); - List listByHyTypeWithoutGuid(String hyType); - List listByZoneId(long zoneId); List getAvailableHypervisorInZone(Long zoneId); - Set getDistictAvailableHypervisorsAcrossClusters(); + List> listDistinctHypervisorsAndArchExcludingExternalType(Long zoneId); List listByDcHyType(long dcId, String hyType); @@ -45,9 +44,21 @@ public interface ClusterDao extends GenericDao { List listClustersWithDisabledPods(long zoneId); + Integer countAllByDcId(long zoneId); + + Integer countAllManagedAndEnabledByDcId(long zoneId); + List listClustersByDcId(long zoneId); - List listAllClusters(Long zoneId); + List listAllClusterIds(Long zoneId); boolean getSupportsResigning(long clusterId); + + List getClustersArchsByZone(long zoneId); + + List listClustersByArchAndZoneId(long zoneId, CPU.CPUArch arch); + + List listDistinctStorageAccessGroups(String name, String keyword); + + List listEnabledClusterIdsByZoneHypervisorArch(Long zoneId, HypervisorType hypervisorType, CPU.CPUArch arch); } diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDaoImpl.java index 4d9bedba9669..c63af0a237ba 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 @@ -16,12 +16,28 @@ // under the License. package com.cloud.dc.dao; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import javax.inject.Inject; + +import org.springframework.stereotype.Component; + +import com.cloud.cpu.CPU; import com.cloud.dc.ClusterDetailsDao; import com.cloud.dc.ClusterDetailsVO; import com.cloud.dc.ClusterVO; import com.cloud.dc.HostPodVO; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.org.Grouping; +import com.cloud.org.Managed; +import com.cloud.utils.Pair; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.JoinBuilder; @@ -31,18 +47,6 @@ import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.exception.CloudRuntimeException; -import org.springframework.stereotype.Component; - -import javax.inject.Inject; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; @Component public class ClusterDaoImpl extends GenericDaoBase implements ClusterDao { @@ -54,7 +58,8 @@ public class ClusterDaoImpl extends GenericDaoBase implements C protected final SearchBuilder ZoneHyTypeSearch; protected final SearchBuilder ZoneClusterSearch; protected final SearchBuilder ClusterSearch; - + protected final SearchBuilder ClusterDistinctArchSearch; + protected final SearchBuilder ClusterArchSearch; protected GenericSearchBuilder ClusterIdSearch; private static final String GET_POD_CLUSTER_MAP_PREFIX = "SELECT pod_id, id FROM cloud.cluster WHERE cluster.id IN( "; @@ -94,6 +99,8 @@ public ClusterDaoImpl() { ZoneClusterSearch = createSearchBuilder(); ZoneClusterSearch.and("dataCenterId", ZoneClusterSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + ZoneClusterSearch.and("allocationState", ZoneClusterSearch.entity().getAllocationState(), Op.EQ); + ZoneClusterSearch.and("managedState", ZoneClusterSearch.entity().getManagedState(), Op.EQ); ZoneClusterSearch.done(); ClusterIdSearch = createSearchBuilder(Long.class); @@ -104,6 +111,16 @@ public ClusterDaoImpl() { ClusterSearch = createSearchBuilder(); ClusterSearch.select(null, Func.DISTINCT, ClusterSearch.entity().getHypervisorType()); ClusterIdSearch.done(); + + ClusterDistinctArchSearch = createSearchBuilder(); + ClusterDistinctArchSearch.and("dataCenterId", ClusterDistinctArchSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + ClusterDistinctArchSearch.select(null, Func.DISTINCT, ClusterDistinctArchSearch.entity().getArch()); + ClusterDistinctArchSearch.done(); + + ClusterArchSearch = createSearchBuilder(); + ClusterArchSearch.and("dataCenterId", ClusterArchSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + ClusterArchSearch.and("arch", ClusterArchSearch.entity().getArch(), SearchCriteria.Op.EQ); + ClusterArchSearch.done(); } @Override @@ -130,14 +147,6 @@ public ClusterVO findBy(String name, long podId) { return findOneBy(sc); } - @Override - public List listByHyTypeWithoutGuid(String hyType) { - SearchCriteria sc = HyTypeWithoutGuidSearch.create(); - sc.setParameters("hypervisorType", hyType); - - return listBy(sc); - } - @Override public List listByDcHyType(long dcId, String hyType) { SearchCriteria sc = ZoneHyTypeSearch.create(); @@ -153,23 +162,36 @@ public List getAvailableHypervisorInZone(Long zoneId) { sc.setParameters("zoneId", zoneId); } List clusters = listBy(sc); - List hypers = new ArrayList(4); - for (ClusterVO cluster : clusters) { - hypers.add(cluster.getHypervisorType()); - } - - return hypers; + return clusters.stream() + .map(ClusterVO::getHypervisorType) + .distinct() + .collect(Collectors.toList()); } + /** + * Returns distinct (HypervisorType, CPUArch) pairs from clusters in the given zone, + * excluding clusters with {@link HypervisorType#External}. + * + * @param zoneId the zone ID to filter by, or {@code null} to include all zones + * @return list of unique hypervisor type and CPU architecture pairs + */ @Override - public Set getDistictAvailableHypervisorsAcrossClusters() { - SearchCriteria sc = ClusterSearch.create(); - List clusters = listBy(sc); - Set hypers = new HashSet<>(); - for (ClusterVO cluster : clusters) { - hypers.add(cluster.getHypervisorType()); + public List> listDistinctHypervisorsAndArchExcludingExternalType(Long zoneId) { + SearchBuilder sb = createSearchBuilder(); + sb.select(null, Func.DISTINCT_PAIR, sb.entity().getHypervisorType(), sb.entity().getArch()); + sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); + sb.and("hypervisorType", sb.entity().getHypervisorType(), SearchCriteria.Op.NEQ); + sb.done(); + SearchCriteria sc = sb.create(); + if (zoneId != null) { + sc.setParameters("zoneId", zoneId); } - return hypers; + sc.setParameters("hypervisorType", HypervisorType.External); + + final List clusters = search(sc, null); + return clusters.stream() + .map(c -> new Pair<>(c.getHypervisorType(), c.getArch())) + .collect(Collectors.toList()); } @Override @@ -252,6 +274,23 @@ public List listClustersWithDisabledPods(long zoneId) { return customSearch(sc, null); } + @Override + public Integer countAllByDcId(long zoneId) { + SearchCriteria sc = ZoneClusterSearch.create(); + sc.setParameters("dataCenterId", zoneId); + return getCount(sc); + } + + @Override + public Integer countAllManagedAndEnabledByDcId(long zoneId) { + SearchCriteria sc = ZoneClusterSearch.create(); + sc.setParameters("dataCenterId", zoneId); + sc.setParameters("allocationState", Grouping.AllocationState.Enabled); + sc.setParameters("managedState", Managed.ManagedState.Managed); + + return getCount(sc); + } + @Override public List listClustersByDcId(long zoneId) { SearchCriteria sc = ZoneClusterSearch.create(); @@ -275,7 +314,7 @@ public boolean remove(Long id) { } @Override - public List listAllClusters(Long zoneId) { + public List listAllClusterIds(Long zoneId) { SearchCriteria sc = ClusterIdSearch.create(); if (zoneId != null) { sc.setParameters("dataCenterId", zoneId); @@ -301,4 +340,77 @@ public boolean getSupportsResigning(long clusterId) { return false; } + + @Override + public List getClustersArchsByZone(long zoneId) { + SearchCriteria sc = ClusterDistinctArchSearch.create(); + sc.setParameters("dataCenterId", zoneId); + List clusters = listBy(sc); + return clusters.stream().map(ClusterVO::getArch).collect(Collectors.toList()); + } + + @Override + public List listClustersByArchAndZoneId(long zoneId, CPU.CPUArch arch) { + SearchCriteria sc = ClusterArchSearch.create(); + sc.setParameters("dataCenterId", zoneId); + sc.setParameters("arch", arch); + return listBy(sc); + } + + @Override + public List listDistinctStorageAccessGroups(String name, String keyword) { + GenericSearchBuilder searchBuilder = createSearchBuilder(String.class); + + searchBuilder.select(null, SearchCriteria.Func.DISTINCT, searchBuilder.entity().getStorageAccessGroups()); + if (name != null) { + searchBuilder.and().op("storageAccessGroupExact", searchBuilder.entity().getStorageAccessGroups(), Op.EQ); + searchBuilder.or("storageAccessGroupPrefix", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + searchBuilder.or("storageAccessGroupSuffix", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + searchBuilder.or("storageAccessGroupMiddle", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + searchBuilder.cp(); + } + if (keyword != null) { + searchBuilder.and("keyword", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + } + searchBuilder.done(); + + SearchCriteria sc = searchBuilder.create(); + if (name != null) { + sc.setParameters("storageAccessGroupExact", name); + sc.setParameters("storageAccessGroupPrefix", name + ",%"); + sc.setParameters("storageAccessGroupSuffix", "%," + name); + sc.setParameters("storageAccessGroupMiddle", "%," + name + ",%"); + } + + if (keyword != null) { + sc.setParameters("keyword", "%" + keyword + "%"); + } + + return customSearch(sc, null); + } + + @Override + public List listEnabledClusterIdsByZoneHypervisorArch(Long zoneId, HypervisorType hypervisorType, CPU.CPUArch arch) { + GenericSearchBuilder sb = createSearchBuilder(Long.class); + sb.selectFields(sb.entity().getId()); + sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); + sb.and("allocationState", sb.entity().getAllocationState(), Op.EQ); + sb.and("managedState", sb.entity().getManagedState(), Op.EQ); + sb.and("hypervisor", sb.entity().getHypervisorType(), Op.EQ); + sb.and("arch", sb.entity().getArch(), Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("allocationState", Grouping.AllocationState.Enabled); + sc.setParameters("managedState", Managed.ManagedState.Managed); + if (zoneId != null) { + sc.setParameters("zoneId", zoneId); + } + if (hypervisorType != null) { + sc.setParameters("hypervisor", hypervisorType); + } + if (arch != null) { + sc.setParameters("arch", arch); + } + return customSearch(sc, null); + } } diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/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/DataCenterDao.java b/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDao.java index dddbce31772d..0ba88f39b233 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDao.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDao.java @@ -117,4 +117,6 @@ public Integer getVlan() { List listAllZones(); List listByIds(List ids); + + List listDistinctStorageAccessGroups(String name, String keyword); } diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDaoImpl.java index 2776b09c2a1c..d8ab12e82e61 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDaoImpl.java @@ -25,6 +25,7 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.utils.db.GenericSearchBuilder; import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; @@ -164,8 +165,8 @@ public int countZoneVlans(long dcId, boolean onlyCountAllocated) { } @Override - public void releasePrivateIpAddress(String ipAddress, long dcId, Long instanceId) { - _ipAllocDao.releaseIpAddress(ipAddress, dcId, instanceId); + public void releasePrivateIpAddress(String ipAddress, long dcId, Long nicId) { + _ipAllocDao.releaseIpAddress(ipAddress, dcId, nicId); } @Override @@ -179,8 +180,8 @@ public void releaseLinkLocalIpAddress(long nicId, String reservationId) { } @Override - public void releaseLinkLocalIpAddress(String ipAddress, long dcId, Long instanceId) { - _linkLocalIpAllocDao.releaseIpAddress(ipAddress, dcId, instanceId); + public void releaseLinkLocalIpAddress(String ipAddress, long dcId, Long nicId) { + _linkLocalIpAllocDao.releaseIpAddress(ipAddress, dcId, nicId); } @Override @@ -226,9 +227,9 @@ public String allocatePodVlan(long podId, long accountId) { } @Override - public PrivateAllocationData allocatePrivateIpAddress(long dcId, long podId, long instanceId, String reservationId, boolean forSystemVms) { - _ipAllocDao.releaseIpAddress(instanceId); - DataCenterIpAddressVO vo = _ipAllocDao.takeIpAddress(dcId, podId, instanceId, reservationId, forSystemVms); + public PrivateAllocationData allocatePrivateIpAddress(long dcId, long podId, long nicId, String reservationId, boolean forSystemVms) { + _ipAllocDao.releaseIpAddress(nicId); + DataCenterIpAddressVO vo = _ipAllocDao.takeIpAddress(dcId, podId, nicId, reservationId, forSystemVms); if (vo == null) { return null; } @@ -242,8 +243,8 @@ public DataCenterIpAddressVO allocatePrivateIpAddress(long dcId, String reservat } @Override - public String allocateLinkLocalIpAddress(long dcId, long podId, long instanceId, String reservationId) { - DataCenterLinkLocalIpAddressVO vo = _linkLocalIpAllocDao.takeIpAddress(dcId, podId, instanceId, reservationId); + public String allocateLinkLocalIpAddress(long dcId, long podId, long nicId, String reservationId) { + DataCenterLinkLocalIpAddressVO vo = _linkLocalIpAllocDao.takeIpAddress(dcId, podId, nicId, reservationId); if (vo == null) { return null; } @@ -441,4 +442,36 @@ public List listByIds(List ids) { sc.setParameters("ids", ids.toArray()); return listBy(sc); } + + @Override + public List listDistinctStorageAccessGroups(String name, String keyword) { + GenericSearchBuilder searchBuilder = createSearchBuilder(String.class); + + searchBuilder.select(null, SearchCriteria.Func.DISTINCT, searchBuilder.entity().getStorageAccessGroups()); + if (name != null) { + searchBuilder.and().op("storageAccessGroupExact", searchBuilder.entity().getStorageAccessGroups(), SearchCriteria.Op.EQ); + searchBuilder.or("storageAccessGroupPrefix", searchBuilder.entity().getStorageAccessGroups(), SearchCriteria.Op.LIKE); + searchBuilder.or("storageAccessGroupSuffix", searchBuilder.entity().getStorageAccessGroups(), SearchCriteria.Op.LIKE); + searchBuilder.or("storageAccessGroupMiddle", searchBuilder.entity().getStorageAccessGroups(), SearchCriteria.Op.LIKE); + searchBuilder.cp(); + } + if (keyword != null) { + searchBuilder.and("keyword", searchBuilder.entity().getStorageAccessGroups(), SearchCriteria.Op.LIKE); + } + searchBuilder.done(); + + SearchCriteria sc = searchBuilder.create(); + if (name != null) { + sc.setParameters("storageAccessGroupExact", name); + sc.setParameters("storageAccessGroupPrefix", name + ",%"); + sc.setParameters("storageAccessGroupSuffix", "%," + name); + sc.setParameters("storageAccessGroupMiddle", "%," + name + ",%"); + } + + if (keyword != null) { + sc.setParameters("keyword", "%" + keyword + "%"); + } + + return customSearch(sc, null); + } } diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDetailsDaoImpl.java index e36c8ebd6c7c..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); @@ -44,9 +45,9 @@ public Scope getScope() { } @Override - public String getConfigValue(long id, ConfigKey key) { - ResourceDetail vo = findDetail(id, key.key()); - return vo == null ? null : vo.getValue(); + public String getConfigValue(long id, String key) { + ResourceDetail vo = findDetail(id, key); + 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 c23137095e6f..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 @@ -51,7 +51,7 @@ public class DataCenterIpAddressDaoImpl extends GenericDaoBase sc = AllFieldsSearch.create(); sc.setParameters("pod", podId); sc.setParameters("taken", (Date)null); @@ -71,7 +71,7 @@ public DataCenterIpAddressVO takeIpAddress(long dcId, long podId, long instanceI return null; } vo.setTakenAt(new Date()); - vo.setInstanceId(instanceId); + vo.setNicId(nicId); vo.setReservationId(reservationId); update(vo.getId(), vo); txn.commit(); @@ -161,24 +161,24 @@ 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 instanceId) { + 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); sc.setParameters("dc", dcId); - sc.setParameters("instance", instanceId); + sc.setParameters("nic", nicId); DataCenterIpAddressVO vo = createForUpdate(); vo.setTakenAt(null); - vo.setInstanceId(null); + vo.setNicId(null); vo.setReservationId(null); update(vo, sc); } @@ -186,15 +186,15 @@ public void releaseIpAddress(String ipAddress, long dcId, Long instanceId) { @Override public void releaseIpAddress(long nicId, String reservationId) { if (logger.isDebugEnabled()) { - logger.debug("Releasing ip address for reservationId=" + reservationId + ", instance=" + nicId); + logger.debug("Releasing IP address for reservationId=" + reservationId + ", nic=" + nicId); } SearchCriteria sc = AllFieldsSearch.create(); - sc.setParameters("instance", nicId); + sc.setParameters("nic", nicId); sc.setParameters("reservation", reservationId); DataCenterIpAddressVO vo = createForUpdate(); vo.setTakenAt(null); - vo.setInstanceId(null); + vo.setNicId(null); vo.setReservationId(null); update(vo, sc); } @@ -202,12 +202,12 @@ 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); vo.setTakenAt(null); - vo.setInstanceId(null); + vo.setNicId(null); vo.setReservationId(null); persist(vo); } @@ -215,14 +215,14 @@ public void releasePodIpAddress(long id) { @Override public void releaseIpAddress(long nicId) { if (logger.isDebugEnabled()) { - logger.debug("Releasing ip address for instance=" + nicId); + logger.debug("Releasing IP address for nic=" + nicId); } SearchCriteria sc = AllFieldsSearch.create(); - sc.setParameters("instance", nicId); + sc.setParameters("nic", nicId); DataCenterIpAddressVO vo = createForUpdate(); vo.setTakenAt(null); - vo.setInstanceId(null); + vo.setNicId(null); vo.setReservationId(null); update(vo, sc); } @@ -294,8 +294,7 @@ public int countIpAddressUsage(final String ipAddress, final long podId, final l sc.addAnd("podId", SearchCriteria.Op.EQ, podId); sc.addAnd("dataCenterId", SearchCriteria.Op.EQ, dcId); - List result = listBy(sc); - return result.size(); + return getCount(sc); } public DataCenterIpAddressDaoImpl() { @@ -305,7 +304,7 @@ public DataCenterIpAddressDaoImpl() { AllFieldsSearch.and("ip", AllFieldsSearch.entity().getIpAddress(), SearchCriteria.Op.EQ); AllFieldsSearch.and("dc", AllFieldsSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); AllFieldsSearch.and("pod", AllFieldsSearch.entity().getPodId(), SearchCriteria.Op.EQ); - AllFieldsSearch.and("instance", AllFieldsSearch.entity().getInstanceId(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("nic", AllFieldsSearch.entity().getNicId(), SearchCriteria.Op.EQ); AllFieldsSearch.and("ipAddress", AllFieldsSearch.entity().getIpAddress(), SearchCriteria.Op.EQ); AllFieldsSearch.and("reservation", AllFieldsSearch.entity().getReservationId(), SearchCriteria.Op.EQ); AllFieldsSearch.and("taken", AllFieldsSearch.entity().getTakenAt(), SearchCriteria.Op.EQ); 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/DataCenterVnetDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterVnetDaoImpl.java index 1c29e6a944c6..ff6682497791 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterVnetDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterVnetDaoImpl.java @@ -81,7 +81,7 @@ public List listAllocatedVnets(long physicalNetworkId) { public int countAllocatedVnets(long physicalNetworkId) { SearchCriteria sc = DcSearchAllocated.create(); sc.setParameters("physicalNetworkId", physicalNetworkId); - return listBy(sc).size(); + return getCount(sc); } @Override diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/HostPodDao.java b/engine/schema/src/main/java/com/cloud/dc/dao/HostPodDao.java index b2e9b898606c..2549a0555e8c 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/HostPodDao.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/HostPodDao.java @@ -34,4 +34,6 @@ public interface HostPodDao extends GenericDao { public List listAllPods(Long zoneId); public List listAllPodsByCidr(long zoneId, String cidr); + + List listDistinctStorageAccessGroups(String name, String keyword); } diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/HostPodDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/HostPodDaoImpl.java index f1835067380f..08901c9a61ea 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/HostPodDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/HostPodDaoImpl.java @@ -143,4 +143,36 @@ public List listAllPodsByCidr(long zoneId, String cidr) { return listBy(sc); } + @Override + public List listDistinctStorageAccessGroups(String name, String keyword) { + GenericSearchBuilder searchBuilder = createSearchBuilder(String.class); + + searchBuilder.select(null, SearchCriteria.Func.DISTINCT, searchBuilder.entity().getStorageAccessGroups()); + if (name != null) { + searchBuilder.and().op("storageAccessGroupExact", searchBuilder.entity().getStorageAccessGroups(), Op.EQ); + searchBuilder.or("storageAccessGroupPrefix", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + searchBuilder.or("storageAccessGroupSuffix", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + searchBuilder.or("storageAccessGroupMiddle", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + searchBuilder.cp(); + } + if (keyword != null) { + searchBuilder.and("keyword", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + } + searchBuilder.done(); + + SearchCriteria sc = searchBuilder.create(); + if (name != null) { + sc.setParameters("storageAccessGroupExact", name); + sc.setParameters("storageAccessGroupPrefix", name + ",%"); + sc.setParameters("storageAccessGroupSuffix", "%," + name); + sc.setParameters("storageAccessGroupMiddle", "%," + name + ",%"); + } + + if (keyword != null) { + sc.setParameters("keyword", "%" + keyword + "%"); + } + + return customSearch(sc, null); + } + } diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/VlanDao.java b/engine/schema/src/main/java/com/cloud/dc/dao/VlanDao.java index 84f38f054412..a6c267bb189b 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/VlanDao.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/VlanDao.java @@ -64,4 +64,6 @@ public interface VlanDao extends GenericDao { List listIpv6RangeByZoneIdAndVlanId(long zoneId, String vlanId); List listIpv6SupportingVlansByZone(long zoneId); + + List listVlansForExternalNetworkProvider(long zoneId, String detailKey); } diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java index 461a9a13b10f..d9fad3cad12a 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java @@ -26,6 +26,7 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.dc.VlanDetailsVO; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; @@ -67,6 +68,8 @@ public class VlanDaoImpl extends GenericDaoBase implements VlanDao protected SearchBuilder ZoneVlanIp6Search; protected SearchBuilder ZoneIp6Search; protected SearchBuilder ZoneVlansSearch; + protected SearchBuilder ProviderVlanSearch; + protected SearchBuilder VlanDetailsProviderSearch; protected SearchBuilder AccountVlanMapSearch; protected SearchBuilder DomainVlanMapSearch; @@ -79,6 +82,8 @@ public class VlanDaoImpl extends GenericDaoBase implements VlanDao protected DomainVlanMapDao _domainVlanMapDao; @Inject protected IPAddressDao _ipAddressDao; + @Inject + protected VlanDetailsDao vlanDetailsDao; @Override public VlanVO findByZoneAndVlanId(long zoneId, String vlanId) { @@ -277,6 +282,19 @@ public boolean configure(String name, Map params) throws Configu ZoneVlansSearch.and("zoneId", ZoneVlansSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); ZoneVlansSearch.and("vlan", ZoneVlansSearch.entity().getVlanTag(), SearchCriteria.Op.IN); ZoneVlansSearch.done(); + + ProviderVlanSearch = createSearchBuilder(); + ProviderVlanSearch.and("removed", ProviderVlanSearch.entity().getRemoved(), SearchCriteria.Op.NULL); + ProviderVlanSearch.and("dataCenterId", ProviderVlanSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + VlanDetailsProviderSearch = vlanDetailsDao.createSearchBuilder(); + VlanDetailsProviderSearch.and("name", VlanDetailsProviderSearch.entity().getName(), SearchCriteria.Op.EQ); + VlanDetailsProviderSearch.and("value", VlanDetailsProviderSearch.entity().getValue(), SearchCriteria.Op.EQ); + ProviderVlanSearch.join("VlanDetailsProviderSearch", VlanDetailsProviderSearch, ProviderVlanSearch.entity().getId(), + VlanDetailsProviderSearch.entity().getResourceId(), JoinBuilder.JoinType.INNER); + + VlanDetailsProviderSearch.done(); + ProviderVlanSearch.done(); + return result; } @@ -434,4 +452,13 @@ public List listIpv6SupportingVlansByZone(long zoneId) { return listBy(sc); } + @Override + public List listVlansForExternalNetworkProvider(long zoneId, String detailKey) { + SearchCriteria sc = ProviderVlanSearch.create(); + sc.setParameters("dataCenterId", zoneId); + sc.setJoinParameters("VlanDetailsProviderSearch", "name", detailKey); + sc.setJoinParameters("VlanDetailsProviderSearch", "value", "true"); + return search(sc, null); + } + } diff --git a/engine/schema/src/main/java/com/cloud/domain/DomainDetailVO.java b/engine/schema/src/main/java/com/cloud/domain/DomainDetailVO.java index df5a2283baa2..6f803cc9f2ff 100644 --- a/engine/schema/src/main/java/com/cloud/domain/DomainDetailVO.java +++ b/engine/schema/src/main/java/com/cloud/domain/DomainDetailVO.java @@ -23,18 +23,18 @@ import javax.persistence.Id; import javax.persistence.Table; -import org.apache.cloudstack.api.InternalIdentity; +import org.apache.cloudstack.api.ResourceDetail; @Entity @Table(name = "domain_details") -public class DomainDetailVO implements InternalIdentity { +public class DomainDetailVO implements ResourceDetail { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private long id; @Column(name = "domain_id") - private long domainId; + private long resourceId; @Column(name = "name") private String name; @@ -46,13 +46,14 @@ protected DomainDetailVO() { } public DomainDetailVO(long domainId, String name, String value) { - this.domainId = domainId; + this.resourceId = domainId; this.name = name; this.value = value; } - public long getDomainId() { - return domainId; + @Override + public long getResourceId() { + return resourceId; } public String getName() { @@ -63,6 +64,11 @@ 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/com/cloud/domain/DomainVO.java b/engine/schema/src/main/java/com/cloud/domain/DomainVO.java index 4c36a3401ca6..c950fa31c881 100644 --- a/engine/schema/src/main/java/com/cloud/domain/DomainVO.java +++ b/engine/schema/src/main/java/com/cloud/domain/DomainVO.java @@ -26,6 +26,7 @@ import javax.persistence.Id; import javax.persistence.Table; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; @@ -206,7 +207,9 @@ public void setState(Domain.State state) { @Override public String toString() { - return new StringBuilder("Domain:").append(id).append(path).toString(); + return String.format("Domain %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "path")); } @Override diff --git a/engine/schema/src/main/java/com/cloud/domain/dao/DomainDao.java b/engine/schema/src/main/java/com/cloud/domain/dao/DomainDao.java index 937d99f50520..3aee371da252 100644 --- a/engine/schema/src/main/java/com/cloud/domain/dao/DomainDao.java +++ b/engine/schema/src/main/java/com/cloud/domain/dao/DomainDao.java @@ -42,5 +42,7 @@ public interface DomainDao extends GenericDao { List getDomainChildrenIds(String path); + List getDomainAndChildrenIds(long domainId); + boolean domainIdListContainsAccessibleDomain(String domainIdList, Account caller, Long domainId); } 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 74f2932ca433..56d971bbe015 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 @@ -19,6 +19,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -238,6 +239,15 @@ public List getDomainChildrenIds(String path) { return customSearch(sc, null); } + @Override + public List getDomainAndChildrenIds(long domainId) { + DomainVO domain = findById(domainId); + if (domain != null) { + return getDomainChildrenIds(domain.getPath()); + } + return new ArrayList<>(); + } + @Override public boolean isChildDomain(Long parentId, Long childId) { if ((parentId == null) || (childId == null)) { diff --git a/engine/schema/src/main/java/com/cloud/domain/dao/DomainDetailsDao.java b/engine/schema/src/main/java/com/cloud/domain/dao/DomainDetailsDao.java index 51362cf885e0..ae149ff43812 100644 --- a/engine/schema/src/main/java/com/cloud/domain/dao/DomainDetailsDao.java +++ b/engine/schema/src/main/java/com/cloud/domain/dao/DomainDetailsDao.java @@ -20,8 +20,9 @@ import com.cloud.domain.DomainDetailVO; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; -public interface DomainDetailsDao extends GenericDao { +public interface DomainDetailsDao extends GenericDao, ResourceDetailsDao { Map findDetails(long domainId); void persist(long domainId, Map details); diff --git a/engine/schema/src/main/java/com/cloud/domain/dao/DomainDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/domain/dao/DomainDetailsDaoImpl.java index dad3fe9ad1eb..5b4e4c591ffc 100644 --- a/engine/schema/src/main/java/com/cloud/domain/dao/DomainDetailsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/domain/dao/DomainDetailsDaoImpl.java @@ -22,20 +22,20 @@ import javax.inject.Inject; +import org.apache.cloudstack.framework.config.ConfigKey.Scope; +import org.apache.cloudstack.framework.config.ScopedConfigStorage; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; + import com.cloud.domain.DomainDetailVO; import com.cloud.domain.DomainVO; -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.SearchCriteria.Op; import com.cloud.utils.db.TransactionLegacy; -import org.apache.cloudstack.framework.config.ConfigKey; -import org.apache.cloudstack.framework.config.ConfigKey.Scope; -import org.apache.cloudstack.framework.config.ScopedConfigStorage; -import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; -public class DomainDetailsDaoImpl extends GenericDaoBase implements DomainDetailsDao, ScopedConfigStorage { +public class DomainDetailsDaoImpl extends ResourceDetailsDaoBase implements DomainDetailsDao, ScopedConfigStorage { protected final SearchBuilder domainSearch; @Inject @@ -45,14 +45,14 @@ public class DomainDetailsDaoImpl extends GenericDaoBase i protected DomainDetailsDaoImpl() { domainSearch = createSearchBuilder(); - domainSearch.and("domainId", domainSearch.entity().getDomainId(), Op.EQ); + domainSearch.and("domainId", domainSearch.entity().getResourceId(), Op.EQ); domainSearch.done(); } @Override public Map findDetails(long domainId) { QueryBuilder sc = QueryBuilder.create(DomainDetailVO.class); - sc.and(sc.entity().getDomainId(), Op.EQ, domainId); + sc.and(sc.entity().getResourceId(), Op.EQ, domainId); List results = sc.list(); Map details = new HashMap(results.size()); for (DomainDetailVO r : results) { @@ -78,11 +78,16 @@ public void persist(long domainId, Map details) { @Override public DomainDetailVO findDetail(long domainId, String name) { QueryBuilder sc = QueryBuilder.create(DomainDetailVO.class); - sc.and(sc.entity().getDomainId(), Op.EQ, domainId); + sc.and(sc.entity().getResourceId(), Op.EQ, domainId); sc.and(sc.entity().getName(), Op.EQ, name); return sc.find(); } + @Override + public void addDetail(long resourceId, String key, String value, boolean display) { + super.addDetail(new DomainDetailVO(resourceId, key, value)); + } + @Override public void deleteDetails(long domainId) { SearchCriteria sc = domainSearch.create(); @@ -106,17 +111,17 @@ public Scope getScope() { } @Override - public String getConfigValue(long id, ConfigKey key) { + public String getConfigValue(long id, String key) { DomainDetailVO vo = null; String enableDomainSettingsForChildDomain = _configDao.getValue("enable.domain.settings.for.child.domain"); if (!Boolean.parseBoolean(enableDomainSettingsForChildDomain)) { - vo = findDetail(id, key.key()); - return vo == null ? null : vo.getValue(); + vo = findDetail(id, key); + return vo == null ? null : getActualValue(vo); } DomainVO domain = _domainDao.findById(id); // if value is not configured in domain then check its parent domain till ROOT while (domain != null) { - vo = findDetail(domain.getId(), key.key()); + vo = findDetail(domain.getId(), key); if (vo != null) { break; } else if (domain.getParent() != null) { @@ -125,6 +130,6 @@ public String getConfigValue(long id, ConfigKey key) { break; } } - return vo == null ? null : vo.getValue(); + return vo == null ? null : getActualValue(vo); } } 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/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/gpu/GpuCardVO.java b/engine/schema/src/main/java/com/cloud/gpu/GpuCardVO.java new file mode 100644 index 000000000000..2410077c84ad --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/GpuCardVO.java @@ -0,0 +1,147 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package com.cloud.gpu; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.gpu.GpuCard; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; +import java.util.UUID; + +@Entity +@Table(name = "gpu_card") +public class GpuCardVO implements GpuCard { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "device_id") + private String deviceId; + + @Column(name = "device_name") + private String deviceName; + + @Column(name = "name") + private String name; + + @Column(name = "vendor_name") + private String vendorName; + + @Column(name = "vendor_id") + private String vendorId; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + public GpuCardVO() { + this.uuid = UUID.randomUUID().toString(); + } + + public GpuCardVO(String deviceId, String deviceName, String name, String vendorName, String vendorId) { + this.uuid = UUID.randomUUID().toString(); + this.deviceId = deviceId; + this.deviceName = deviceName; + this.name = name; + this.vendorName = vendorName; + this.vendorId = vendorId; + this.created = new Date(); + } + + @Override + public String toString() { + return String.format("GPUCard %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "deviceId", "deviceName", "vendorId", "vendorName")); + } + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + @Override + public String getDeviceName() { + return deviceName; + } + + public void setDeviceName(String deviceName) { + this.deviceName = deviceName; + } + + @Override + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String getVendorName() { + return vendorName; + } + + public void setVendorName(String vendorName) { + this.vendorName = vendorName; + } + + @Override + public String getVendorId() { + return vendorId; + } + + public void setVendorId(String vendorId) { + this.vendorId = vendorId; + } + + @Override + public Date getCreated() { + return created; + } + + @Override + public String getGroupName() { + return "Group of " + getVendorName() + " " + getDeviceName() + " GPUs"; + } +} diff --git a/engine/schema/src/main/java/com/cloud/gpu/GpuDeviceVO.java b/engine/schema/src/main/java/com/cloud/gpu/GpuDeviceVO.java new file mode 100644 index 000000000000..ac20e74c3604 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/GpuDeviceVO.java @@ -0,0 +1,200 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.gpu; + +import org.apache.cloudstack.gpu.GpuDevice; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.UUID; + +@Entity +@Table(name = "gpu_device") +public class GpuDeviceVO implements GpuDevice { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "card_id") + private long cardId; + + @Column(name = "vgpu_profile_id") + private long vgpuProfileId; + + @Column(name = "bus_address") + private String busAddress; + + @Column(name = "host_id") + private long hostId; + + @Column(name = "vm_id") + private Long vmId; + + @Column(name = "type") + @Enumerated(value = EnumType.STRING) + private DeviceType type = DeviceType.PCI; + + @Column(name = "state") + @Enumerated(value = EnumType.STRING) + private State state = State.Free; + + @Column(name = "managed_state") + @Enumerated(value = EnumType.STRING) + private ManagedState managedState = ManagedState.Managed; + + @Column(name = "parent_gpu_device_id") + private Long parentGpuDeviceId; + + @Column(name = "numa_node") + private String numaNode; + + @Column(name = "pci_root") + private String pciRoot; + + public GpuDeviceVO() { + this.uuid = UUID.randomUUID().toString(); + } + + public GpuDeviceVO(long cardId, long vgpuProfileId, String busAddress, long hostId, Long parentGpuDeviceId, + String numaNode, String pciRoot) { + this.uuid = UUID.randomUUID().toString(); + this.cardId = cardId; + this.vgpuProfileId = vgpuProfileId; + this.busAddress = busAddress; + this.hostId = hostId; + this.parentGpuDeviceId = parentGpuDeviceId; + this.numaNode = numaNode; + this.pciRoot = pciRoot; + } + + @Override + public String toString() { + return String.format("GpuDevice %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "cardId", "vgpuProfileId", "busAddress", "hostId", "vmId", + "parentGpuDeviceId", "numaNode", "pciRoot", "state", "resourceState")); + } + + @Override + public long getId() { + return id; + } + + public String getUuid() { + return uuid; + } + + public long getCardId() { + return cardId; + } + + public void setCardId(long cardId) { + this.cardId = cardId; + } + + public long getVgpuProfileId() { + return vgpuProfileId; + } + + public void setVgpuProfileId(long vgpuProfileId) { + this.vgpuProfileId = vgpuProfileId; + } + + public String getBusAddress() { + return busAddress; + } + + public void setBusAddress(String busAddress) { + this.busAddress = busAddress; + } + + public long getHostId() { + return hostId; + } + + public void setHostId(long hostId) { + this.hostId = hostId; + } + + @Override + public State getState() { + return state; + } + + public void setState(State state) { + this.state = state; + } + + public DeviceType getType() { + return type; + } + + public void setType(DeviceType type) { + this.type = type; + } + + public ManagedState getManagedState() { + return managedState; + } + + public void setManagedState(ManagedState managedState) { + this.managedState = managedState; + } + + public Long getVmId() { + return vmId; + } + + public void setVmId(Long vmId) { + this.vmId = vmId; + } + + public Long getParentGpuDeviceId() { + return parentGpuDeviceId; + } + + public void setParentGpuDeviceId(Long parentGpuDeviceId) { + this.parentGpuDeviceId = parentGpuDeviceId; + } + + public String getNumaNode() { + return numaNode; + } + + public void setNumaNode(String numaNode) { + this.numaNode = numaNode; + } + + public String getPciRoot() { + return pciRoot; + } + + public void setPciRoot(String pciRoot) { + this.pciRoot = pciRoot; + } +} diff --git a/engine/schema/src/main/java/com/cloud/gpu/VGPUTypesVO.java b/engine/schema/src/main/java/com/cloud/gpu/VGPUTypesVO.java index 5bbf90854ee6..4944d51f1b44 100644 --- a/engine/schema/src/main/java/com/cloud/gpu/VGPUTypesVO.java +++ b/engine/schema/src/main/java/com/cloud/gpu/VGPUTypesVO.java @@ -40,19 +40,19 @@ public class VGPUTypesVO implements InternalIdentity { private String vgpuType; @Column(name="video_ram") - private long videoRam; + private Long videoRam; @Column(name="max_heads") - private long maxHeads; + private Long maxHeads; @Column(name="max_resolution_x") - private long maxResolutionX; + private Long maxResolutionX; @Column(name="max_resolution_y") - private long maxResolutionY; + private Long maxResolutionY; @Column(name="max_vgpu_per_pgpu") - private long maxVgpuPerPgpu; + private Long maxVgpuPerPgpu; @Column(name="remaining_capacity") private long remainingCapacity; @@ -63,7 +63,7 @@ public class VGPUTypesVO implements InternalIdentity { protected VGPUTypesVO() { } - public VGPUTypesVO(long gpuGroupId, String vgpuType, long videoRam, long maxHeads, long maxResolutionX, long maxResolutionY, long maxVgpuPerPgpu, + public VGPUTypesVO(long gpuGroupId, String vgpuType, Long videoRam, Long maxHeads, Long maxResolutionX, Long maxResolutionY, Long maxVgpuPerPgpu, long remainingCapacity, long maxCapacity) { this.gpuGroupId = gpuGroupId; this.vgpuType = vgpuType; @@ -92,43 +92,43 @@ public void setVgpuType(String vgpuType) { this.vgpuType = vgpuType; } - public long getVideoRam() { + public Long getVideoRam() { return videoRam; } - public void setVideoRam(long videoRam) { + public void setVideoRam(Long videoRam) { this.videoRam = videoRam; } - public long getMaxHeads() { + public Long getMaxHeads() { return maxHeads; } - public void setMaxHeads(long maxHeads) { + public void setMaxHeads(Long maxHeads) { this.maxHeads = maxHeads; } - public long getMaxResolutionX() { + public Long getMaxResolutionX() { return maxResolutionX; } - public void setMaxResolutionX(long maxResolutionX) { + public void setMaxResolutionX(Long maxResolutionX) { this.maxResolutionX = maxResolutionX; } - public long getMaxResolutionY() { + public Long getMaxResolutionY() { return maxResolutionY; } - public void setMaxResolutionY(long maxResolutionY) { + public void setMaxResolutionY(Long maxResolutionY) { this.maxResolutionY = maxResolutionY; } - public long getMaxVgpuPerPgpu() { + public Long getMaxVgpuPerPgpu() { return maxVgpuPerPgpu; } - public void setMaxVgpuPerPgpu(long maxVgpuPerPgpu) { + public void setMaxVgpuPerPgpu(Long maxVgpuPerPgpu) { this.maxVgpuPerPgpu = maxVgpuPerPgpu; } diff --git a/engine/schema/src/main/java/com/cloud/gpu/VgpuProfileVO.java b/engine/schema/src/main/java/com/cloud/gpu/VgpuProfileVO.java new file mode 100644 index 000000000000..86f5eb94415b --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/VgpuProfileVO.java @@ -0,0 +1,191 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package com.cloud.gpu; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.gpu.VgpuProfile; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; +import java.util.UUID; + +@Entity +@Table(name = "vgpu_profile") +public class VgpuProfileVO implements VgpuProfile { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "name") + private String name; + + @Column(name = "description") + private String description; + + @Column(name = "card_id") + private Long cardId; + + @Column(name = "max_vgpu_per_pgpu") + private Long maxVgpuPerPgpu; + + @Column(name = "video_ram") + private Long videoRam; + + @Column(name = "max_heads") + private Long maxHeads; + + @Column(name = "max_resolution_x") + private Long maxResolutionX; + + @Column(name = "max_resolution_y") + private Long maxResolutionY; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + public VgpuProfileVO() { + this.uuid = UUID.randomUUID().toString(); + } + + public VgpuProfileVO(String name, String description, Long gpuCardId, Long maxVgpuPerPgpu) { + this.uuid = UUID.randomUUID().toString(); + this.name = name; + this.description = description; + this.cardId = gpuCardId; + this.maxVgpuPerPgpu = maxVgpuPerPgpu; + this.created = new Date(); + } + + + public VgpuProfileVO(String name, String description, Long gpuCardId, Long maxVgpuPerPgpu, Long videoRam, Long maxHeads, Long maxResolutionX, Long maxResolutionY) { + this.uuid = UUID.randomUUID().toString(); + this.name = name; + this.description = description; + this.cardId = gpuCardId; + this.maxVgpuPerPgpu = maxVgpuPerPgpu; + this.videoRam = videoRam; + this.maxHeads = maxHeads; + this.maxResolutionX = maxResolutionX; + this.maxResolutionY = maxResolutionY; + this.created = new Date(); + } + + @Override + public String toString() { + return String.format("VgpuProfile %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "cardId")); + } + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @Override + public Date getCreated() { + return created; + } + + @Override + public Long getCardId() { + return cardId; + } + + public void setCardId(Long cardId) { + this.cardId = cardId; + } + + @Override + public Long getMaxVgpuPerPgpu() { + return maxVgpuPerPgpu; + } + + public void setMaxVgpuPerPgpu(Long maxVgpuPerPgpu) { + this.maxVgpuPerPgpu = maxVgpuPerPgpu; + } + + @Override + public Long getVideoRam() { + return videoRam; + } + + public void setVideoRam(Long videoRam) { + this.videoRam = videoRam; + } + + @Override + public Long getMaxHeads() { + return maxHeads; + } + + public void setMaxHeads(Long maxHeads) { + this.maxHeads = maxHeads; + } + + @Override + public Long getMaxResolutionX() { + return maxResolutionX; + } + + public void setMaxResolutionX(Long maxResolutionX) { + this.maxResolutionX = maxResolutionX; + } + + @Override + public Long getMaxResolutionY() { + return maxResolutionY; + } + + public void setMaxResolutionY(Long maxResolutionY) { + this.maxResolutionY = maxResolutionY; + } +} diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/GpuCardDao.java b/engine/schema/src/main/java/com/cloud/gpu/dao/GpuCardDao.java new file mode 100644 index 000000000000..4463a690ae92 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/GpuCardDao.java @@ -0,0 +1,39 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.gpu.dao; + +import com.cloud.gpu.GpuCardVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.GenericDao; + +import java.util.List; + +public interface GpuCardDao extends GenericDao { + + /** + * Find GPU card by vendor and device id + * + * @param vendorId the vendor id + * @param deviceId the device id + * @return GpuCardVO + */ + GpuCardVO findByVendorIdAndDeviceId(String vendorId, String deviceId); + + Pair, Integer> searchAndCountGpuCards( + Long id, String keyword, String vendorId, String vendorName, + String deviceId, String deviceName, boolean activeOnly, Long startIndex, Long pageSize); +} diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/GpuCardDaoImpl.java b/engine/schema/src/main/java/com/cloud/gpu/dao/GpuCardDaoImpl.java new file mode 100644 index 000000000000..8aad85d45086 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/GpuCardDaoImpl.java @@ -0,0 +1,122 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.gpu.dao; + +import com.cloud.gpu.GpuCardVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.util.List; + +@Component +public class GpuCardDaoImpl extends GenericDaoBase implements GpuCardDao { + + private final SearchBuilder allFieldSearch; + + @Inject + private GpuDeviceDao gpuDeviceDao; + + public GpuCardDaoImpl() { + allFieldSearch = createSearchBuilder(); + allFieldSearch.and("name", allFieldSearch.entity().getName(), SearchCriteria.Op.EQ); + allFieldSearch.and("vendorId", allFieldSearch.entity().getVendorId(), SearchCriteria.Op.EQ); + allFieldSearch.and("vendorName", allFieldSearch.entity().getVendorName(), SearchCriteria.Op.EQ); + allFieldSearch.and("deviceId", allFieldSearch.entity().getDeviceId(), SearchCriteria.Op.EQ); + allFieldSearch.and("deviceName", allFieldSearch.entity().getDeviceName(), SearchCriteria.Op.EQ); + allFieldSearch.done(); + } + + @Override + public GpuCardVO findByVendorIdAndDeviceId(String vendorId, String deviceId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters("vendorId", vendorId); + sc.setParameters("deviceId", deviceId); + return findOneBy(sc); + } + + @Override + public Pair, Integer> searchAndCountGpuCards(Long id, String keyword, String vendorId, + String vendorName, String deviceId, String deviceName, boolean activeOnly, Long startIndex, Long pageSize + ) { + + Filter searchFilter = new Filter(GpuCardVO.class, "id", true, startIndex, pageSize); + SearchBuilder sb = createSearchBuilder(); + + if (id != null) { + sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); + } + if (keyword != null) { + sb.op("nameKeyword", sb.entity().getName(), SearchCriteria.Op.LIKE); + sb.and("deviceNameKeyword", sb.entity().getDeviceName(), SearchCriteria.Op.LIKE); + sb.and("vendorNameKeyword", sb.entity().getVendorName(), SearchCriteria.Op.LIKE); + sb.cp(); + } + if (vendorId != null) { + sb.and("vendorId", sb.entity().getVendorId(), SearchCriteria.Op.EQ); + } + if (vendorName != null) { + sb.and("vendorName", sb.entity().getVendorName(), SearchCriteria.Op.EQ); + } + if (deviceId != null) { + sb.and("deviceId", sb.entity().getDeviceId(), SearchCriteria.Op.EQ); + } + if (deviceName != null) { + sb.and("deviceName", sb.entity().getDeviceName(), SearchCriteria.Op.EQ); + } + if (activeOnly) { + sb.and("ids", sb.entity().getId(), SearchCriteria.Op.IN); + } + sb.done(); + + // Build search criteria + SearchCriteria sc = sb.create(); + if (id != null) { + sc.setParameters("id", id); + } + if (keyword != null) { + sc.setParameters("nameKeyword", "%" + keyword + "%"); + sc.setParameters("deviceNameKeyword", "%" + keyword + "%"); + sc.setParameters("vendorNameKeyword", "%" + keyword + "%"); + } + if (vendorId != null) { + sc.setParameters("vendorId", vendorId); + } + if (vendorName != null) { + sc.setParameters("vendorName", vendorName); + } + if (deviceId != null) { + sc.setParameters("deviceId", deviceId); + } + if (deviceName != null) { + sc.setParameters("deviceName", deviceName); + } + if (activeOnly) { + List cardIds = gpuDeviceDao.getDistinctGpuCardIds(); + if (cardIds.isEmpty()) { + return new Pair<>(List.of(), 0); + } + sc.setParameters("ids", cardIds.toArray()); + } + + return searchAndCount(sc, searchFilter); + } +} diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/GpuDeviceDao.java b/engine/schema/src/main/java/com/cloud/gpu/dao/GpuDeviceDao.java new file mode 100644 index 000000000000..e362f23888d3 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/GpuDeviceDao.java @@ -0,0 +1,71 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.gpu.dao; + +import com.cloud.gpu.GpuDeviceVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.GenericDao; + +import java.util.List; + +public interface GpuDeviceDao extends GenericDao { + + List listByIds(List ids); + + /** + * Find GPU device by host ID and bus address + * + * @param hostId the host ID + * @param busAddress the PCI bus address + * @return GpuDeviceVO + */ + GpuDeviceVO findByHostIdAndBusAddress(long hostId, String busAddress); + + /** + * List GPU devices by host ID + * + * @param hostId the ID of the host + * @return a list of GPU devices for the host + */ + List listByHostId(long hostId); + + /** + * List GPU devices by VM ID + * + * @param vmId the VM ID + * @return list of GpuDeviceVO + */ + List listByVmId(long vmId); + + boolean isVgpuProfileInUse(long vgpuProfileId); + + boolean isGpuCardInUse(long cardId); + + List listByHostAndVm(Long hostId, long vmId); + + List listDevicesForAllocation(Long hostId, Long vgpuProfileId); + + Pair, Integer> searchAndCountGpuDevices( + Long id, String keyword, Long hostId, Long vmId, Long gpuCardId, Long vgpuProfileId, + Long startIndex, Long pageSize); + + List getDistinctGpuCardIds(); + + List getDistinctVgpuProfileIds(); + + List listByParentGpuDeviceId(Long parentGpuDeviceId); +} diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/GpuDeviceDaoImpl.java b/engine/schema/src/main/java/com/cloud/gpu/dao/GpuDeviceDaoImpl.java new file mode 100644 index 000000000000..bd7032aff27b --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/GpuDeviceDaoImpl.java @@ -0,0 +1,260 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.gpu.dao; + +import com.cloud.gpu.GpuCardVO; +import com.cloud.gpu.GpuDeviceVO; +import com.cloud.gpu.VgpuProfileVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.JoinBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.apache.cloudstack.gpu.GpuDevice; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Component +public class GpuDeviceDaoImpl extends GenericDaoBase implements GpuDeviceDao { + + private static final String IDS = "ids"; + private static final String HOST_ID = "hostId"; + private static final String VM_ID = "vmId"; + private static final String BUS_ADDRESS = "busAddress"; + private static final String CARD_ID = "cardId"; + private static final String VGPU_PROFILE_ID = "vgpuProfileId"; + private static final String PARENT_GPU_DEVICE_ID = "parentGpuDeviceId"; + private static final String STATE = "state"; + private static final String MANAGED_STATE = "managedState"; + private static final String TYPE = "type"; + private final SearchBuilder allFieldSearch; + private SearchBuilder devicesForAllocationSearch; + @Inject + private GpuCardDao gpuCardDao; + @Inject + private VgpuProfileDao vgpuProfileDao; + + public GpuDeviceDaoImpl() { + allFieldSearch = createSearchBuilder(); + allFieldSearch.and(IDS, allFieldSearch.entity().getId(), SearchCriteria.Op.IN); + allFieldSearch.and(HOST_ID, allFieldSearch.entity().getHostId(), SearchCriteria.Op.EQ); + allFieldSearch.and(CARD_ID, allFieldSearch.entity().getCardId(), SearchCriteria.Op.EQ); + allFieldSearch.and(BUS_ADDRESS, allFieldSearch.entity().getBusAddress(), SearchCriteria.Op.EQ); + allFieldSearch.and(STATE, allFieldSearch.entity().getState(), SearchCriteria.Op.EQ); + allFieldSearch.and(VGPU_PROFILE_ID, allFieldSearch.entity().getVgpuProfileId(), SearchCriteria.Op.EQ); + allFieldSearch.and(PARENT_GPU_DEVICE_ID, allFieldSearch.entity().getParentGpuDeviceId(), SearchCriteria.Op.EQ); + allFieldSearch.and(VM_ID, allFieldSearch.entity().getVmId(), SearchCriteria.Op.EQ); + allFieldSearch.done(); + + devicesForAllocationSearch = createSearchBuilder(); + devicesForAllocationSearch.and(HOST_ID, devicesForAllocationSearch.entity().getHostId(), SearchCriteria.Op.EQ); + devicesForAllocationSearch.and(VGPU_PROFILE_ID, devicesForAllocationSearch.entity().getVgpuProfileId(), SearchCriteria.Op.IN); + devicesForAllocationSearch.and(STATE, devicesForAllocationSearch.entity().getState(), SearchCriteria.Op.EQ); + devicesForAllocationSearch.and(MANAGED_STATE, devicesForAllocationSearch.entity().getManagedState(), SearchCriteria.Op.EQ); + devicesForAllocationSearch.and(TYPE, devicesForAllocationSearch.entity().getType(), SearchCriteria.Op.NEQ); + devicesForAllocationSearch.done(); + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + return super.configure(name, params); + } + + @Override + public List listByIds(List ids) { + if (CollectionUtils.isEmpty(ids)) { + return Collections.emptyList(); + } + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters(IDS, ids.toArray()); + return listBy(sc); + } + + @Override + public GpuDeviceVO findByHostIdAndBusAddress(long hostId, String busAddress) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters(HOST_ID, hostId); + sc.setParameters(BUS_ADDRESS, busAddress); + return findOneBy(sc); + } + + @Override + public List listByHostId(long hostId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters(HOST_ID, hostId); + return listBy(sc); + } + + @Override + public List listByVmId(long vmId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters(VM_ID, vmId); + return listBy(sc); + } + + @Override + public boolean isVgpuProfileInUse(long vgpuProfileId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters(VGPU_PROFILE_ID, vgpuProfileId); + return getCount(sc) > 0; + } + + @Override + public boolean isGpuCardInUse(long cardId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters(CARD_ID, cardId); + return getCount(sc) > 0; + } + + @Override + public List listByHostAndVm(Long hostId, long vmId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters(HOST_ID, hostId); + sc.setParameters(VM_ID, vmId); + return search(sc, null); + } + + @Override + public List listDevicesForAllocation(Long hostId, Long vgpuProfileId) { + SearchCriteria sc = devicesForAllocationSearch.create(); + sc.setParameters(HOST_ID, hostId); + sc.setParameters(VGPU_PROFILE_ID, vgpuProfileId); + sc.setParameters(STATE, GpuDevice.State.Free); + sc.setParameters(MANAGED_STATE, GpuDevice.ManagedState.Managed); + sc.setParameters(TYPE, GpuDevice.DeviceType.VGPUOnly); + return search(sc, null); + } + + @Override + public Pair, Integer> searchAndCountGpuDevices(Long id, String keyword, Long hostId, Long vmId, + Long gpuCardId, Long vgpuProfileId, Long startIndex, Long pageSize) { + Filter searchFilter = new Filter(GpuDeviceVO.class, "id", true, startIndex, pageSize); + SearchBuilder sb = createSearchBuilder(); + + if (id != null) { + sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); + } + if (hostId != null) { + sb.and("hostId", sb.entity().getHostId(), SearchCriteria.Op.EQ); + } + if (vmId != null) { + sb.and("vmId", sb.entity().getVmId(), SearchCriteria.Op.EQ); + } + if (gpuCardId != null) { + sb.and("cardId", sb.entity().getCardId(), SearchCriteria.Op.EQ); + } + if (vgpuProfileId != null) { + sb.and("vgpuProfileId", sb.entity().getVgpuProfileId(), SearchCriteria.Op.EQ); + } + if (keyword != null) { + SearchBuilder cardSb = gpuCardDao.createSearchBuilder(); + SearchBuilder profileSb = vgpuProfileDao.createSearchBuilder(); + sb.join("cardJoin", cardSb, sb.entity().getCardId(), cardSb.entity().getId(), JoinBuilder.JoinType.INNER); + sb.join("profileJoin", profileSb, sb.entity().getCardId(), profileSb.entity().getId(), + JoinBuilder.JoinType.INNER); + + sb.op("cardNameKeyword", cardSb.entity().getName(), SearchCriteria.Op.LIKE); + sb.or("cardNameKeyword", cardSb.entity().getVendorName(), SearchCriteria.Op.LIKE); + sb.or("cardNameKeyword", cardSb.entity().getDeviceName(), SearchCriteria.Op.LIKE); + + sb.op("profileNameKeyword", profileSb.entity().getName(), SearchCriteria.Op.LIKE); + sb.op("profileDescriptionKeyword", profileSb.entity().getDescription(), SearchCriteria.Op.LIKE); + sb.cp(); + } + + sb.done(); + + // Build search criteria + SearchCriteria sc = sb.create(); + if (id != null) { + sc.setParameters("id", id); + } + if (hostId != null) { + sc.setParameters("hostId", hostId); + } + if (vmId != null) { + sc.setParameters("vmId", vmId); + } + if (gpuCardId != null) { + sc.setParameters("cardId", gpuCardId); + } + if (vgpuProfileId != null) { + sc.setParameters("vgpuProfileId", vgpuProfileId); + } + + if (keyword != null) { + sc.setJoinParameters("cardJoin", "cardNameKeyword", "%" + keyword + "%"); + sc.setJoinParameters("cardJoin", "cardNameKeyword", "%" + keyword + "%"); + sc.setJoinParameters("cardJoin", "cardNameKeyword", "%" + keyword + "%"); + sc.setJoinParameters("profileJoin", "profileNameKeyword", "%" + keyword + "%"); + sc.setJoinParameters("profileJoin", "profileDescriptionKeyword", "%" + keyword + "%"); + } + + return searchAndCount(sc, searchFilter); + } + + @Override + public List getDistinctGpuCardIds() { + SearchBuilder sb = createSearchBuilder(); + sb.select(null, SearchCriteria.Func.DISTINCT, sb.entity().getCardId()); + sb.done(); + SearchCriteria sc = sb.create(); + + List gpuDevices = listBy(sc); + if (CollectionUtils.isEmpty(gpuDevices)) { + return Collections.emptyList(); + } + + return gpuDevices.stream() + .map(GpuDeviceVO::getCardId) + .distinct() + .collect(Collectors.toList()); + } + + @Override + public List getDistinctVgpuProfileIds() { + SearchBuilder sb = createSearchBuilder(); + sb.select(null, SearchCriteria.Func.DISTINCT, sb.entity().getVgpuProfileId()); + sb.done(); + SearchCriteria sc = sb.create(); + + List gpuDevices = listBy(sc); + if (CollectionUtils.isEmpty(gpuDevices)) { + return Collections.emptyList(); + } + + return gpuDevices.stream() + .map(GpuDeviceVO::getVgpuProfileId) + .distinct() + .collect(Collectors.toList()); + } + + @Override + public List listByParentGpuDeviceId(Long parentGpuDeviceId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters(PARENT_GPU_DEVICE_ID, parentGpuDeviceId); + return listBy(sc); + } +} diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/HostGpuGroupsDao.java b/engine/schema/src/main/java/com/cloud/gpu/dao/HostGpuGroupsDao.java index 8e4f2f742ac5..99e336175393 100644 --- a/engine/schema/src/main/java/com/cloud/gpu/dao/HostGpuGroupsDao.java +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/HostGpuGroupsDao.java @@ -19,6 +19,7 @@ import java.util.List; import com.cloud.gpu.HostGpuGroupsVO; +import com.cloud.utils.Pair; import com.cloud.utils.db.GenericDao; public interface HostGpuGroupsDao extends GenericDao { @@ -57,4 +58,15 @@ public interface HostGpuGroupsDao extends GenericDao { */ void persist(long hostId, List gpuGroups); + + /** + * Returns max and remaining GPU capacity + * + * @param dcId + * @param podId + * @param clusterId + * @param hostId + * @return Pair containing max GPU capacity and remaining GPU capacity + */ + Pair getGpuStats(Long dcId, Long podId, Long clusterId, Long hostId); } diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/HostGpuGroupsDaoImpl.java b/engine/schema/src/main/java/com/cloud/gpu/dao/HostGpuGroupsDaoImpl.java index 30535c7e27d5..343b144597c5 100644 --- a/engine/schema/src/main/java/com/cloud/gpu/dao/HostGpuGroupsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/HostGpuGroupsDaoImpl.java @@ -16,9 +16,16 @@ // under the License. package com.cloud.gpu.dao; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; import java.util.List; +import com.cloud.utils.Pair; +import com.cloud.utils.db.TransactionLegacy; +import com.cloud.utils.exception.CloudRuntimeException; import org.springframework.stereotype.Component; import com.cloud.gpu.HostGpuGroupsVO; @@ -87,4 +94,75 @@ public void deleteGpuEntries(long hostId) { sc.setParameters("hostId", hostId); remove(sc); } + + @Override + public Pair getGpuStats(Long dcId, Long podId, Long clusterId, Long hostId) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + Pair result = null; + List resourceIdList = new ArrayList<>(); + String query = getStatsQuery(resourceIdList, dcId, podId, clusterId, hostId); + + try { + PreparedStatement pstmt = txn.prepareAutoCloseStatement(query); + for (int i = 0; i < resourceIdList.size(); i++) { + pstmt.setLong(1 + i, resourceIdList.get(i)); + } + + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + result = new Pair<>(rs.getLong(1), rs.getLong(2)); + } + return result; + } catch (SQLException e) { + throw new CloudRuntimeException("Error while fetching GPU stats: " + e.getMessage(), e); + } catch (Throwable e) { + throw new CloudRuntimeException("Caught: " + query, e); + } + } + + private String getStatsQuery(List resourceIdList, Long dcId, Long podId, Long clusterId, Long hostId) { + StringBuilder query = new StringBuilder("SELECT SUM(max_capacity), SUM(remaining_capacity)" + + "FROM vgpu_types " + + "WHERE" + + " gpu_group_id IN (" + + " SELECT" + + " host_gpu_groups.id" + + " FROM" + + " host_gpu_groups" + + " INNER JOIN host ON host.id = host_gpu_groups.host_id "); + if (dcId != null) { + query.append("WHERE host.data_center_id = ? "); + resourceIdList.add(dcId); + } + + if (podId != null) { + if (resourceIdList.isEmpty()) { + query.append("WHERE "); + } else { + query.append("AND "); + } + query.append(" host.pod_id = ? "); + resourceIdList.add(podId); + } + if (clusterId != null) { + if (resourceIdList.isEmpty()) { + query.append("WHERE "); + } else { + query.append("AND "); + } + query.append(" host.cluster_id = ? "); + resourceIdList.add(clusterId); + } + if (hostId != null) { + if (resourceIdList.isEmpty()) { + query.append("WHERE "); + } else { + query.append("AND "); + } + query.append(" host.id = ? "); + resourceIdList.add(hostId); + } + query.append(" )"); + return query.toString(); + } } diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/VGPUTypesDaoImpl.java b/engine/schema/src/main/java/com/cloud/gpu/dao/VGPUTypesDaoImpl.java index edc5e1f67c86..524feed24679 100644 --- a/engine/schema/src/main/java/com/cloud/gpu/dao/VGPUTypesDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/VGPUTypesDaoImpl.java @@ -16,19 +16,6 @@ //under the License. package com.cloud.gpu.dao; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map.Entry; - -import javax.inject.Inject; - -import org.springframework.stereotype.Component; - import com.cloud.agent.api.VgpuTypesInfo; import com.cloud.gpu.HostGpuGroupsVO; import com.cloud.gpu.VGPUTypesVO; @@ -37,30 +24,42 @@ import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.exception.CloudRuntimeException; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map.Entry; @Component public class VGPUTypesDaoImpl extends GenericDaoBase implements VGPUTypesDao { - private final SearchBuilder _searchByGroupId; - private final SearchBuilder _searchByGroupIdVGPUType; - - @Inject protected HostGpuGroupsDao _hostGpuGroupsDao; + @Inject + protected HostGpuGroupsDao hostGpuGroupsDao; private static final String LIST_ZONE_POD_CLUSTER_WIDE_GPU_CAPACITIES = "SELECT host_gpu_groups.group_name, vgpu_type, max_vgpu_per_pgpu, SUM(remaining_capacity) AS remaining_capacity, SUM(max_capacity) AS total_capacity FROM" + - " `cloud`.`vgpu_types` INNER JOIN `cloud`.`host_gpu_groups` ON vgpu_types.gpu_group_id = host_gpu_groups.id INNER JOIN `cloud`.`host`" + - " ON host_gpu_groups.host_id = host.id WHERE host.type = 'Routing' AND host.data_center_id = ?"; + " `cloud`.`vgpu_types` INNER JOIN `cloud`.`host_gpu_groups` ON vgpu_types.gpu_group_id = host_gpu_groups.id INNER JOIN `cloud`.`host`" + + " ON host_gpu_groups.host_id = host.id WHERE host.type = 'Routing' AND vgpu_types.max_capacity > 0 AND host.data_center_id = ?"; + + private final SearchBuilder searchByGroupId; + private final SearchBuilder searchByGroupIdVGPUType; public VGPUTypesDaoImpl() { - _searchByGroupId = createSearchBuilder(); - _searchByGroupId.and("groupId", _searchByGroupId.entity().getGpuGroupId(), SearchCriteria.Op.EQ); - _searchByGroupId.done(); + searchByGroupId = createSearchBuilder(); + searchByGroupId.and("groupId", searchByGroupId.entity().getGpuGroupId(), SearchCriteria.Op.EQ); + searchByGroupId.done(); - _searchByGroupIdVGPUType = createSearchBuilder(); - _searchByGroupIdVGPUType.and("groupId", _searchByGroupIdVGPUType.entity().getGpuGroupId(), SearchCriteria.Op.EQ); - _searchByGroupIdVGPUType.and("vgpuType", _searchByGroupIdVGPUType.entity().getVgpuType(), SearchCriteria.Op.EQ); - _searchByGroupIdVGPUType.done(); + searchByGroupIdVGPUType = createSearchBuilder(); + searchByGroupIdVGPUType.and("groupId", searchByGroupIdVGPUType.entity().getGpuGroupId(), SearchCriteria.Op.EQ); + searchByGroupIdVGPUType.and("vgpuType", searchByGroupIdVGPUType.entity().getVgpuType(), SearchCriteria.Op.EQ); + searchByGroupIdVGPUType.done(); } @Override @@ -83,7 +82,7 @@ public List listGPUCapacities(Long dcId, Long podId, Long cluster finalQuery.append(" AND host.cluster_id = ?"); resourceIdList.add(clusterId); } - finalQuery.append(" GROUP BY host_gpu_groups.group_name, vgpu_type"); + finalQuery.append(" GROUP BY host_gpu_groups.group_name, vgpu_type, max_vgpu_per_pgpu"); try { pstmt = txn.prepareAutoCloseStatement(finalQuery.toString()); @@ -106,14 +105,14 @@ public List listGPUCapacities(Long dcId, Long podId, Long cluster @Override public List listByGroupId(long groupId) { - SearchCriteria sc = _searchByGroupId.create(); + SearchCriteria sc = searchByGroupId.create(); sc.setParameters("groupId", groupId); return listBy(sc); } @Override public VGPUTypesVO findByGroupIdVGPUType(long groupId, String vgpuType) { - SearchCriteria sc = _searchByGroupIdVGPUType.create(); + SearchCriteria sc = searchByGroupIdVGPUType.create(); sc.setParameters("groupId", groupId); sc.setParameters("vgpuType", vgpuType); return findOneBy(sc); @@ -124,7 +123,7 @@ public void persist(long hostId, HashMap> Iterator>> it1 = groupDetails.entrySet().iterator(); while (it1.hasNext()) { Entry> entry = it1.next(); - HostGpuGroupsVO gpuGroup = _hostGpuGroupsDao.findByHostIdGroupName(hostId, entry.getKey()); + HostGpuGroupsVO gpuGroup = hostGpuGroupsDao.findByHostIdGroupName(hostId, entry.getKey()); HashMap values = entry.getValue(); Iterator> it2 = values.entrySet().iterator(); while (it2.hasNext()) { diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/VgpuProfileDao.java b/engine/schema/src/main/java/com/cloud/gpu/dao/VgpuProfileDao.java new file mode 100644 index 000000000000..2628f1851f22 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/VgpuProfileDao.java @@ -0,0 +1,33 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.gpu.dao; + +import com.cloud.gpu.VgpuProfileVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.GenericDao; + +import java.util.List; + +public interface VgpuProfileDao extends GenericDao { + + VgpuProfileVO findByNameAndCardId(String name, long cardId); + + int removeByCardId(long cardId); + + Pair, Integer> searchAndCountVgpuProfiles(Long id, String name, String keyword, Long gpuCardId, + boolean activeOnly, Long startIndex, Long pageSize); +} diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/VgpuProfileDaoImpl.java b/engine/schema/src/main/java/com/cloud/gpu/dao/VgpuProfileDaoImpl.java new file mode 100644 index 000000000000..11dd7edb30d5 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/VgpuProfileDaoImpl.java @@ -0,0 +1,110 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.gpu.dao; + +import com.cloud.gpu.VgpuProfileVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.util.List; + +@Component +public class VgpuProfileDaoImpl extends GenericDaoBase implements VgpuProfileDao { + + private final SearchBuilder allFieldSearch; + + @Inject + private GpuDeviceDao gpuDeviceDao; + + public VgpuProfileDaoImpl() { + allFieldSearch = createSearchBuilder(); + allFieldSearch.and("name", allFieldSearch.entity().getName(), SearchCriteria.Op.EQ); + allFieldSearch.and("cardId", allFieldSearch.entity().getCardId(), SearchCriteria.Op.IN); + allFieldSearch.done(); + } + + @Override + public VgpuProfileVO findByNameAndCardId(String name, long cardId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters("name", name); + sc.setParameters("cardId", cardId); + return findOneBy(sc); + } + + @Override + public int removeByCardId(long cardId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters("cardId", cardId); + return remove(sc); + } + + @Override + public Pair, Integer> searchAndCountVgpuProfiles(Long id, String name, String keyword, + Long gpuCardId, boolean activeOnly, Long startIndex, Long pageSize) { + Filter searchFilter = new Filter(VgpuProfileVO.class, "id", true, startIndex, pageSize); + SearchBuilder sb = createSearchBuilder(); + + if (id != null) { + sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); + } + if (name != null) { + sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ); + } + if (keyword != null) { + sb.and("keywordName", sb.entity().getName(), SearchCriteria.Op.LIKE); + sb.and("keywordDescription", sb.entity().getDescription(), SearchCriteria.Op.LIKE); + } + if (gpuCardId != null) { + sb.and("cardId", sb.entity().getCardId(), SearchCriteria.Op.EQ); + } + if (activeOnly) { + sb.and("ids", sb.entity().getId(), SearchCriteria.Op.IN); + } + sb.done(); + + // Build search criteria + SearchCriteria sc = sb.create(); + if (id != null) { + sc.setParameters("id", id); + } + if (name != null) { + sc.setParameters("name", name); + } + if (keyword != null) { + sc.setParameters("keywordName", "%" + keyword + "%"); + sc.setParameters("keywordDescription", "%" + keyword + "%"); + } + if (gpuCardId != null) { + sc.setParameters("cardId", gpuCardId); + } + + if (activeOnly) { + List vgpuProfileIds = gpuDeviceDao.getDistinctVgpuProfileIds(); + if (vgpuProfileIds.isEmpty()) { + return new Pair<>(List.of(), 0); + } + sc.setParameters("ids", vgpuProfileIds.toArray()); + } + + return searchAndCount(sc, searchFilter); + } +} diff --git a/engine/schema/src/main/java/com/cloud/host/HostTagVO.java b/engine/schema/src/main/java/com/cloud/host/HostTagVO.java index cd4ac29738d5..98071a2c0732 100644 --- a/engine/schema/src/main/java/com/cloud/host/HostTagVO.java +++ b/engine/schema/src/main/java/com/cloud/host/HostTagVO.java @@ -40,6 +40,9 @@ public class HostTagVO implements InternalIdentity { @Column(name = "tag") private String tag; + @Column(name = "is_implicit") + private boolean isImplicit = false; + @Column(name = "is_tag_a_rule") private boolean isTagARule; @@ -74,6 +77,13 @@ public boolean getIsTagARule() { return isTagARule; } + public void setIsImplicit(boolean isImplicit) { + this.isImplicit = isImplicit; + } + + public boolean getIsImplicit() { + return isImplicit; + } @Override public long getId() { 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 b7cac8ff8cbc..d51b4eca0577 100644 --- a/engine/schema/src/main/java/com/cloud/host/HostVO.java +++ b/engine/schema/src/main/java/com/cloud/host/HostVO.java @@ -16,10 +16,13 @@ // under the License. package com.cloud.host; +import java.util.Arrays; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; import javax.persistence.Column; @@ -39,21 +42,25 @@ import javax.persistence.TemporalType; import javax.persistence.Transient; +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.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang.BooleanUtils; +import org.apache.commons.lang3.StringUtils; + import com.cloud.agent.api.VgpuTypesInfo; import com.cloud.host.dao.HostTagsDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.offering.ServiceOffering; import com.cloud.resource.ResourceState; import com.cloud.storage.Storage.StoragePoolType; +import com.cloud.template.VirtualMachineTemplate; import com.cloud.util.StoragePoolTypeConverter; import com.cloud.utils.NumbersUtil; import com.cloud.utils.db.GenericDao; -import java.util.Arrays; - -import org.apache.cloudstack.utils.jsinterpreter.TagAsRuleHelper; -import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; -import org.apache.commons.lang.BooleanUtils; -import org.apache.commons.lang3.StringUtils; @Entity @Table(name = "host") @@ -122,7 +129,7 @@ public class HostVO implements Host { private String storageMacAddressDeux; @Column(name = "hypervisor_type", updatable = true, nullable = false) - @Enumerated(value = EnumType.STRING) + @Convert(converter = HypervisorTypeConverter.class) private HypervisorType hypervisorType; @Column(name = "proxy_port") @@ -148,12 +155,19 @@ public class HostVO implements Host { @Column(name = "hypervisor_version") private String hypervisorVersion; + @Column(name = "arch") + @Convert(converter = CPUArchConverter.class) + private CPU.CPUArch arch; + @Column(name = "update_count", updatable = true, nullable = false) protected long updated; // This field should be updated everytime the state is updated. There's no set method in the vo object because it is done with in the dao code. @Column(name = "uuid") private String uuid; + @Column(name = "storage_access_groups") + String storageAccessGroups; + // This is a delayed load value. If the value is null, // then this field has not been loaded yet. // Call host dao to load it. @@ -346,6 +360,15 @@ public Boolean getIsTagARule() { return isTagARule; } + @Override + public String getStorageAccessGroups() { + return storageAccessGroups; + } + + public void setStorageAccessGroups(String storageAccessGroups) { + this.storageAccessGroups = storageAccessGroups; + } + public HashMap> getGpuGroupDetails() { return groupDetails; } @@ -393,6 +416,9 @@ public void setGpuGroups(HashMap> groupDe @Column(name = "mgmt_server_id") private Long managementServerId; + @Column(name = "last_mgmt_server_id") + private Long lastManagementServerId; + @Column(name = "dom0_memory") private long dom0MinMemory; @@ -559,6 +585,10 @@ public void setManagementServerId(Long managementServerId) { this.managementServerId = managementServerId; } + public void setLastManagementServerId(Long lastManagementServerId) { + this.lastManagementServerId = lastManagementServerId; + } + @Override public long getLastPinged() { return lastPinged; @@ -628,6 +658,11 @@ public Long getManagementServerId() { return managementServerId; } + @Override + public Long getLastManagementServerId() { + return lastManagementServerId; + } + @Override public Date getDisconnectedOn() { return disconnectedOn; @@ -701,7 +736,7 @@ public boolean equals(Object obj) { @Override public String toString() { - return String.format("Host %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "name", "uuid", "type")); + return String.format("Host %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "uuid", "name", "type")); } public void setHypervisorType(HypervisorType hypervisorType) { @@ -733,6 +768,15 @@ public ResourceState getResourceState() { return resourceState; } + @Override + public CPU.CPUArch getArch() { + return arch; + } + + public void setArch(CPU.CPUArch arch) { + this.arch = arch; + } + public void setResourceState(ResourceState state) { resourceState = state; } @@ -764,7 +808,49 @@ public void setUuid(String uuid) { this.uuid = uuid; } - public boolean checkHostServiceOfferingTags(ServiceOffering serviceOffering){ + private Set getHostServiceOfferingAndTemplateStrictTags(ServiceOffering serviceOffering, VirtualMachineTemplate template, Set strictHostTags) { + if (StringUtils.isEmpty(serviceOffering.getHostTag()) && StringUtils.isEmpty(template.getTemplateTag())) { + return new HashSet<>(); + } + List hostTagsList = getHostTags(); + HashSet hostTagsSet = CollectionUtils.isNotEmpty(hostTagsList) ? new HashSet<>(hostTagsList) : new HashSet<>(); + HashSet tags = new HashSet<>(); + if (StringUtils.isNotEmpty(serviceOffering.getHostTag())) { + tags.addAll(Arrays.asList(serviceOffering.getHostTag().split(","))); + } + if (StringUtils.isNotEmpty(template.getTemplateTag())) { + tags.add(template.getTemplateTag()); + } + tags.removeIf(tag -> !strictHostTags.contains(tag)); + tags.removeAll(hostTagsSet); + return tags; + } + + public boolean checkHostServiceOfferingAndTemplateTags(ServiceOffering serviceOffering, VirtualMachineTemplate template, Set strictHostTags) { + if (serviceOffering == null || template == null) { + return false; + } + Set tags = getHostServiceOfferingAndTemplateStrictTags(serviceOffering, template, strictHostTags); + if (tags.isEmpty()) { + return true; + } + List hostTagsList = getHostTags(); + HashSet hostTagsSet = CollectionUtils.isNotEmpty(hostTagsList) ? new HashSet<>(hostTagsList) : new HashSet<>(); + return hostTagsSet.containsAll(tags); + } + + public Set getHostServiceOfferingAndTemplateMissingTags(ServiceOffering serviceOffering, VirtualMachineTemplate template, Set strictHostTags) { + Set tags = getHostServiceOfferingAndTemplateStrictTags(serviceOffering, template, strictHostTags); + if (tags.isEmpty()) { + return new HashSet<>(); + } + List hostTagsList = getHostTags(); + HashSet hostTagsSet = CollectionUtils.isNotEmpty(hostTagsList) ? new HashSet<>(hostTagsList) : new HashSet<>(); + tags.removeAll(hostTagsSet); + return tags; + } + + public boolean checkHostServiceOfferingTags(ServiceOffering serviceOffering) { if (serviceOffering == null) { return false; } @@ -776,7 +862,6 @@ public boolean checkHostServiceOfferingTags(ServiceOffering serviceOffering){ if (StringUtils.isEmpty(serviceOffering.getHostTag())) { return true; } - List serviceOfferingTags = Arrays.asList(serviceOffering.getHostTag().split(",")); return this.getHostTags() != null && this.getHostTags().containsAll(serviceOfferingTags); } 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 fe30722feb18..090b019334f4 100644 --- a/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java +++ b/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java @@ -19,6 +19,7 @@ import java.util.Date; import java.util.List; +import com.cloud.cpu.CPU; import com.cloud.host.Host; import com.cloud.host.Host.Type; import com.cloud.host.HostVO; @@ -27,8 +28,10 @@ import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.info.RunningHostCountInfo; import com.cloud.resource.ResourceState; +import com.cloud.utils.Pair; import com.cloud.utils.db.GenericDao; import com.cloud.utils.fsm.StateDao; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; /** * Data Access Object for server @@ -39,8 +42,14 @@ public interface HostDao extends GenericDao, StateDao status); + Integer countAllByTypeInZone(long zoneId, final Host.Type type); + Integer countUpAndEnabledHostsInZone(long zoneId); + + Pair countAllHostsAndCPUSocketsByType(Type type); + /** * Mark all hosts associated with a certain management server * as disconnected. @@ -63,7 +72,7 @@ public interface HostDao extends GenericDao, StateDao listByHostTag(Host.Type type, Long clusterId, Long podId, long dcId, String hostTag); + List listByHostTag(Host.Type type, Long clusterId, Long podId, Long dcId, String hostTag); List findAndUpdateApplianceToLoad(long lastPingSecondsAfter, long managementServerId); @@ -75,32 +84,49 @@ public interface HostDao extends GenericDao, StateDao findHypervisorHostInCluster(long clusterId); + List findHypervisorHostInPod(long podId); + + List findHypervisorHostInZone(long zoneId); + + HostVO findAnyStateHypervisorHostInCluster(long clusterId); + HostVO findOldestExistentHypervisorHostInCluster(long clusterId); List listAllUpAndEnabledNonHAHosts(Type type, Long clusterId, Long podId, long dcId, String haTag); List findByDataCenterId(Long zoneId); + List listIdsByDataCenterId(Long zoneId); + List findByPodId(Long podId); + List findByPodId(Long podId, Type type); + + List listIdsByPodId(Long podId); + List findByClusterId(Long clusterId); - List findByClusterIdAndEncryptionSupport(Long clusterId); + List findByClusterId(Long clusterId, Type type); - /** - * Returns hosts that are 'Up' and 'Enabled' from the given Data Center/Zone - */ - List listByDataCenterId(long id); + List listIdsByClusterId(Long clusterId); + + List listIdsForUpRouting(Long zoneId, Long podId, Long clusterId); + + List listIdsByType(Type type); + + List listIdsForUpEnabledByZoneAndHypervisor(Long zoneId, HypervisorType hypervisorType); + + List findByClusterIdAndEncryptionSupport(Long clusterId); /** - * Returns hosts that are from the given Data Center/Zone and at a given state (e.g. Creating, Enabled, Disabled, etc). + * Returns host Ids that are 'Up' and 'Enabled' from the given Data Center/Zone */ - List listByDataCenterIdAndState(long id, ResourceState state); + List listEnabledIdsByDataCenterId(long id); /** - * Returns hosts that are 'Up' and 'Disabled' from the given Data Center/Zone + * Returns host Ids that are 'Up' and 'Disabled' from the given Data Center/Zone */ - List listDisabledByDataCenterId(long id); + List listDisabledIdsByDataCenterId(long id); List listByDataCenterIdAndHypervisorType(long zoneId, Hypervisor.HypervisorType hypervisorType); @@ -110,8 +136,6 @@ public interface HostDao extends GenericDao, StateDao listAllHostsThatHaveNoRuleTag(Host.Type type, Long clusterId, Long podId, Long dcId); - List listAllHostsByType(Host.Type type); - HostVO findByPublicIp(String publicIp); List listClustersByHostTag(String hostTagOnOffering); @@ -141,6 +165,8 @@ public interface HostDao extends GenericDao, StateDao listByHostCapability(Host.Type type, Long clusterId, Long podId, long dcId, String hostCapabilty); + List listByClusterHypervisorTypeAndHostCapability(Long clusterId, HypervisorType hypervisorType, String hostCapabilty); + List listByClusterAndHypervisorType(long clusterId, HypervisorType hypervisorType); HostVO findByName(String name); @@ -149,12 +175,40 @@ public interface HostDao extends GenericDao, StateDao listHostsWithActiveVMs(long offeringId); + List listHostsByMsAndDc(long msId, long dcId); + + List listHostsByMsDcResourceState(long msId, long dcId, List excludedResourceStates); + + List listHostsByMs(long msId); + + List listHostsByMsResourceState(long msId, List excludedResourceStates); + + /** + * Count Hosts by given Management Server, Host and Hypervisor Types, + * and exclude Hosts with given Resource States. + * + * @param msId Management Server Id + * @param excludedResourceStates Resource States to be excluded + * @param hostTypes Host Types + * @param hypervisorTypes Hypervisor Types + * @return Hosts count + */ + int countHostsByMsResourceStateTypeAndHypervisorType(long msId, List excludedResourceStates, + List hostTypes, List hypervisorTypes); + + /** + * Retrieves the host ids/agents this {@see ManagementServer} has responsibility over. + * @param msId the id of the {@see ManagementServer} + * @return the host ids/agents this {@see ManagementServer} has responsibility over + */ + List listByMs(long msId); + /** - * Retrieves the number of hosts/agents this {@see ManagementServer} has responsibility over. - * @param msid the id of the {@see ManagementServer} - * @return the number of hosts/agents this {@see ManagementServer} has responsibility over + * Retrieves the last host ids/agents this {@see ManagementServer} has responsibility over. + * @param msId the id of the {@see ManagementServer} + * @return the last host ids/agents this {@see ManagementServer} has responsibility over */ - int countByMs(long msid); + List listByLastMs(long msId); /** * Retrieves the hypervisor versions of the hosts in the datacenter which are in Up state in ascending order @@ -167,4 +221,24 @@ public interface HostDao extends GenericDao, StateDao findHostsWithTagRuleThatMatchComputeOferringTags(String computeOfferingTags); List findClustersThatMatchHostTagRule(String computeOfferingTags); + + List listSsvmHostsWithPendingMigrateJobsOrderedByJobCount(); + + boolean isHostUp(long hostId); + + List findHostIdsByZoneClusterResourceStateTypeAndHypervisorType(final Long zoneId, final Long clusterId, + final Long msId, final List resourceStates, final List types, + final List hypervisorTypes); + + List listDistinctHypervisorTypes(final Long zoneId); + + List> listDistinctHypervisorArchTypes(final Long zoneId); + + List listDistinctArchTypes(final Long clusterId); + + List listByIds(final List ids); + + Long findClusterIdByVolumeInfo(VolumeInfo volumeInfo); + + List listDistinctStorageAccessGroups(String name, String keyword); } diff --git a/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java b/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java index 9eadab76b2c2..2d8fcca6cdb7 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 @@ -20,6 +20,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -34,6 +35,8 @@ import javax.inject.Inject; import javax.persistence.TableGenerator; +import com.cloud.vm.VirtualMachine; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.utils.jsinterpreter.TagAsRuleHelper; import org.apache.commons.collections.CollectionUtils; @@ -41,12 +44,13 @@ import com.cloud.cluster.agentlb.HostTransferMapVO; import com.cloud.cluster.agentlb.dao.HostTransferMapDao; import com.cloud.configuration.ManagementServiceConfiguration; +import com.cloud.cpu.CPU; import com.cloud.dc.ClusterVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.gpu.dao.HostGpuGroupsDao; import com.cloud.gpu.dao.VGPUTypesDao; -import com.cloud.host.Host; import com.cloud.host.DetailVO; +import com.cloud.host.Host; import com.cloud.host.Host.Type; import com.cloud.host.HostTagVO; import com.cloud.host.HostVO; @@ -59,6 +63,8 @@ import com.cloud.org.Managed; import com.cloud.resource.ResourceState; import com.cloud.utils.DateUtil; +import com.cloud.utils.Pair; +import com.cloud.utils.StringUtils; import com.cloud.utils.db.Attribute; import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; @@ -66,6 +72,7 @@ import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.JoinBuilder; import com.cloud.utils.db.JoinBuilder.JoinType; +import com.cloud.utils.db.QueryBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Func; @@ -73,20 +80,19 @@ import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.db.UpdateBuilder; import com.cloud.utils.exception.CloudRuntimeException; - -import java.util.Arrays; +import org.apache.commons.lang3.ObjectUtils; @DB @TableGenerator(name = "host_req_sq", table = "op_host", pkColumnName = "id", valueColumnName = "sequence", allocationSize = 1) public class HostDaoImpl extends GenericDaoBase implements HostDao { //FIXME: , ExternalIdDao { - private static final String LIST_HOST_IDS_BY_COMPUTETAGS = "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) AS filtered " - + "WHERE tag IN(%s) AND is_tag_a_rule = 0 " + 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 " + + "WHERE tag IN (%s) AND (is_tag_a_rule = 0 OR is_tag_a_rule IS NULL) " + "GROUP BY host_id " + "HAVING tag_count = %s "; private static final String SEPARATOR = ","; - private static final String LIST_CLUSTERID_FOR_HOST_TAG = "select distinct cluster_id from host join ( %s ) AS selected_hosts ON host.id = selected_hosts.host_id"; + private static final String LIST_CLUSTER_IDS_FOR_HOST_TAGS = "select distinct cluster_id from host join ( %s ) AS selected_hosts ON host.id = selected_hosts.host_id"; private static final String GET_HOSTS_OF_ACTIVE_VMS = "select h.id " + "from vm_instance vm " + "join host h on (vm.host_id=h.id) " + @@ -98,10 +104,11 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao protected SearchBuilder TypePodDcStatusSearch; + protected SearchBuilder IdsSearch; protected SearchBuilder IdStatusSearch; protected SearchBuilder TypeDcSearch; protected SearchBuilder TypeDcStatusSearch; - protected SearchBuilder TypeClusterStatusSearch; + protected SearchBuilder TypeStatusStateSearch; protected SearchBuilder MsStatusSearch; protected SearchBuilder DcPrivateIpAddressSearch; protected SearchBuilder DcStorageIpAddressSearch; @@ -124,7 +131,11 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao protected SearchBuilder UnmanagedApplianceSearch; protected SearchBuilder MaintenanceCountSearch; protected SearchBuilder HostTypeCountSearch; - protected SearchBuilder ResponsibleMsCountSearch; + protected SearchBuilder ResponsibleMsSearch; + protected SearchBuilder ResponsibleMsDcSearch; + protected GenericSearchBuilder ResponsibleMsIdSearch; + protected GenericSearchBuilder LastMsIdSearch; + protected SearchBuilder HostTypeClusterCountSearch; protected SearchBuilder HostTypeZoneCountSearch; protected SearchBuilder ClusterStatusSearch; protected SearchBuilder TypeNameZoneSearch; @@ -136,8 +147,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao protected SearchBuilder ManagedRoutingServersSearch; protected SearchBuilder SecondaryStorageVMSearch; - protected GenericSearchBuilder HostIdSearch; - protected GenericSearchBuilder HostsInStatusSearch; + protected GenericSearchBuilder HostsInStatusesSearch; protected GenericSearchBuilder CountRoutingByDc; protected SearchBuilder HostTransferSearch; protected SearchBuilder ClusterManagedSearch; @@ -187,11 +197,35 @@ public void init() { HostTypeCountSearch = createSearchBuilder(); HostTypeCountSearch.and("type", HostTypeCountSearch.entity().getType(), SearchCriteria.Op.EQ); + HostTypeCountSearch.and("zoneId", HostTypeCountSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + HostTypeCountSearch.and("resourceState", HostTypeCountSearch.entity().getResourceState(), SearchCriteria.Op.EQ); HostTypeCountSearch.done(); - ResponsibleMsCountSearch = createSearchBuilder(); - ResponsibleMsCountSearch.and("managementServerId", ResponsibleMsCountSearch.entity().getManagementServerId(), SearchCriteria.Op.EQ); - ResponsibleMsCountSearch.done(); + ResponsibleMsSearch = createSearchBuilder(); + ResponsibleMsSearch.and("managementServerId", ResponsibleMsSearch.entity().getManagementServerId(), SearchCriteria.Op.EQ); + ResponsibleMsSearch.done(); + + ResponsibleMsDcSearch = createSearchBuilder(); + ResponsibleMsDcSearch.and("managementServerId", ResponsibleMsDcSearch.entity().getManagementServerId(), SearchCriteria.Op.EQ); + ResponsibleMsDcSearch.and("dcId", ResponsibleMsDcSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + ResponsibleMsDcSearch.done(); + + ResponsibleMsIdSearch = createSearchBuilder(String.class); + ResponsibleMsIdSearch.selectFields(ResponsibleMsIdSearch.entity().getUuid()); + ResponsibleMsIdSearch.and("managementServerId", ResponsibleMsIdSearch.entity().getManagementServerId(), SearchCriteria.Op.EQ); + ResponsibleMsIdSearch.done(); + + LastMsIdSearch = createSearchBuilder(String.class); + LastMsIdSearch.selectFields(LastMsIdSearch.entity().getUuid()); + LastMsIdSearch.and("lastManagementServerId", LastMsIdSearch.entity().getLastManagementServerId(), SearchCriteria.Op.EQ); + LastMsIdSearch.done(); + + HostTypeClusterCountSearch = createSearchBuilder(); + HostTypeClusterCountSearch.and("cluster", HostTypeClusterCountSearch.entity().getClusterId(), SearchCriteria.Op.EQ); + HostTypeClusterCountSearch.and("type", HostTypeClusterCountSearch.entity().getType(), SearchCriteria.Op.EQ); + HostTypeClusterCountSearch.and("status", HostTypeClusterCountSearch.entity().getStatus(), SearchCriteria.Op.IN); + HostTypeClusterCountSearch.and("removed", HostTypeClusterCountSearch.entity().getRemoved(), SearchCriteria.Op.NULL); + HostTypeClusterCountSearch.done(); HostTypeZoneCountSearch = createSearchBuilder(); HostTypeZoneCountSearch.and("type", HostTypeZoneCountSearch.entity().getType(), SearchCriteria.Op.EQ); @@ -233,12 +267,18 @@ public void init() { TypeDcStatusSearch.and("resourceState", TypeDcStatusSearch.entity().getResourceState(), SearchCriteria.Op.EQ); TypeDcStatusSearch.done(); - TypeClusterStatusSearch = createSearchBuilder(); - TypeClusterStatusSearch.and("type", TypeClusterStatusSearch.entity().getType(), SearchCriteria.Op.EQ); - TypeClusterStatusSearch.and("cluster", TypeClusterStatusSearch.entity().getClusterId(), SearchCriteria.Op.EQ); - TypeClusterStatusSearch.and("status", TypeClusterStatusSearch.entity().getStatus(), SearchCriteria.Op.EQ); - TypeClusterStatusSearch.and("resourceState", TypeClusterStatusSearch.entity().getResourceState(), SearchCriteria.Op.EQ); - TypeClusterStatusSearch.done(); + TypeStatusStateSearch = createSearchBuilder(); + TypeStatusStateSearch.and("type", TypeStatusStateSearch.entity().getType(), SearchCriteria.Op.EQ); + TypeStatusStateSearch.and("cluster", TypeStatusStateSearch.entity().getClusterId(), SearchCriteria.Op.EQ); + TypeStatusStateSearch.and("pod", TypeStatusStateSearch.entity().getPodId(), SearchCriteria.Op.EQ); + TypeStatusStateSearch.and("zone", TypeStatusStateSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + TypeStatusStateSearch.and("status", TypeStatusStateSearch.entity().getStatus(), SearchCriteria.Op.EQ); + TypeStatusStateSearch.and("resourceState", TypeStatusStateSearch.entity().getResourceState(), SearchCriteria.Op.EQ); + TypeStatusStateSearch.done(); + + IdsSearch = createSearchBuilder(); + IdsSearch.and("id", IdsSearch.entity().getId(), SearchCriteria.Op.IN); + IdsSearch.done(); IdStatusSearch = createSearchBuilder(); IdStatusSearch.and("id", IdStatusSearch.entity().getId(), SearchCriteria.Op.EQ); @@ -291,10 +331,12 @@ public void init() { PodSearch = createSearchBuilder(); PodSearch.and("podId", PodSearch.entity().getPodId(), SearchCriteria.Op.EQ); + PodSearch.and("type", PodSearch.entity().getType(), Op.EQ); PodSearch.done(); ClusterSearch = createSearchBuilder(); ClusterSearch.and("clusterId", ClusterSearch.entity().getClusterId(), SearchCriteria.Op.EQ); + ClusterSearch.and("type", ClusterSearch.entity().getType(), Op.EQ); ClusterSearch.done(); TypeSearch = createSearchBuilder(); @@ -341,6 +383,7 @@ public void init() { ClusterHypervisorSearch.and("hypervisor", ClusterHypervisorSearch.entity().getHypervisorType(), SearchCriteria.Op.EQ); ClusterHypervisorSearch.and("type", ClusterHypervisorSearch.entity().getType(), SearchCriteria.Op.EQ); ClusterHypervisorSearch.and("status", ClusterHypervisorSearch.entity().getStatus(), SearchCriteria.Op.EQ); + ClusterHypervisorSearch.and("resourceState", ClusterHypervisorSearch.entity().getResourceState(), SearchCriteria.Op.EQ); ClusterHypervisorSearch.done(); UnmanagedDirectConnectSearch = createSearchBuilder(); @@ -385,14 +428,14 @@ public void init() { AvailHypevisorInZone.groupBy(AvailHypevisorInZone.entity().getHypervisorType()); AvailHypevisorInZone.done(); - HostsInStatusSearch = createSearchBuilder(Long.class); - HostsInStatusSearch.selectFields(HostsInStatusSearch.entity().getId()); - HostsInStatusSearch.and("dc", HostsInStatusSearch.entity().getDataCenterId(), Op.EQ); - HostsInStatusSearch.and("pod", HostsInStatusSearch.entity().getPodId(), Op.EQ); - HostsInStatusSearch.and("cluster", HostsInStatusSearch.entity().getClusterId(), Op.EQ); - HostsInStatusSearch.and("type", HostsInStatusSearch.entity().getType(), Op.EQ); - HostsInStatusSearch.and("statuses", HostsInStatusSearch.entity().getStatus(), Op.IN); - HostsInStatusSearch.done(); + HostsInStatusesSearch = createSearchBuilder(Long.class); + HostsInStatusesSearch.selectFields(HostsInStatusesSearch.entity().getId()); + HostsInStatusesSearch.and("dc", HostsInStatusesSearch.entity().getDataCenterId(), Op.EQ); + HostsInStatusesSearch.and("pod", HostsInStatusesSearch.entity().getPodId(), Op.EQ); + HostsInStatusesSearch.and("cluster", HostsInStatusesSearch.entity().getClusterId(), Op.EQ); + HostsInStatusesSearch.and("type", HostsInStatusesSearch.entity().getType(), Op.EQ); + HostsInStatusesSearch.and("statuses", HostsInStatusesSearch.entity().getStatus(), Op.IN); + HostsInStatusesSearch.done(); CountRoutingByDc = createSearchBuilder(Long.class); CountRoutingByDc.select(null, Func.COUNT, null); @@ -455,11 +498,6 @@ public void init() { HostsInClusterSearch.and("server", HostsInClusterSearch.entity().getManagementServerId(), SearchCriteria.Op.NNULL); HostsInClusterSearch.done(); - HostIdSearch = createSearchBuilder(Long.class); - HostIdSearch.selectFields(HostIdSearch.entity().getId()); - HostIdSearch.and("dataCenterId", HostIdSearch.entity().getDataCenterId(), Op.EQ); - HostIdSearch.done(); - searchBuilderFindByRuleTag = _hostTagsDao.createSearchBuilder(); searchBuilderFindByRuleTag.and("is_tag_a_rule", searchBuilderFindByRuleTag.entity().getIsTagARule(), Op.EQ); searchBuilderFindByRuleTag.or("tagDoesNotExist", searchBuilderFindByRuleTag.entity().getIsTagARule(), Op.NULL); @@ -491,8 +529,7 @@ public long countBy(long clusterId, ResourceState... states) { sc.setParameters("resourceState", (Object[])states); sc.setParameters("cluster", clusterId); - List hosts = listBy(sc); - return hosts.size(); + return getCount(sc); } @Override @@ -502,37 +539,63 @@ public Integer countAllByType(final Host.Type type) { return getCount(sc); } + @Override + public Integer countAllInClusterByTypeAndStates(Long clusterId, final Host.Type type, List status) { + SearchCriteria sc = HostTypeClusterCountSearch.create(); + if (clusterId != null) { + sc.setParameters("cluster", clusterId); + } + if (type != null) { + sc.setParameters("type", type); + } + if (status != null) { + sc.setParameters("status", status.toArray()); + } + return getCount(sc); + } + @Override public Integer countAllByTypeInZone(long zoneId, Type type) { SearchCriteria sc = HostTypeCountSearch.create(); sc.setParameters("type", type); - sc.setParameters("dc", zoneId); + sc.setParameters("zoneId", zoneId); return getCount(sc); } @Override - public List listByDataCenterId(long id) { - return listByDataCenterIdAndState(id, ResourceState.Enabled); + public Integer countUpAndEnabledHostsInZone(long zoneId) { + SearchCriteria sc = HostTypeCountSearch.create(); + sc.setParameters("type", Type.Routing); + sc.setParameters("resourceState", ResourceState.Enabled); + sc.setParameters("zoneId", zoneId); + return getCount(sc); } @Override - public List listByDataCenterIdAndState(long id, ResourceState state) { - SearchCriteria sc = scHostsFromZoneUpRouting(id); - sc.setParameters("resourceState", state); - return listBy(sc); + public Pair countAllHostsAndCPUSocketsByType(Type type) { + GenericSearchBuilder sb = createSearchBuilder(SumCount.class); + sb.select("sum", Func.SUM, sb.entity().getCpuSockets()); + sb.select("count", Func.COUNT, null); + sb.and("type", sb.entity().getType(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("type", type); + SumCount result = customSearch(sc, null).get(0); + return new Pair<>((int)result.count, (int)result.sum); + } + + private List listIdsForRoutingByZoneIdAndResourceState(long zoneId, ResourceState state) { + return listIdsBy(Type.Routing, Status.Up, state, null, zoneId, null, null); } @Override - public List listDisabledByDataCenterId(long id) { - return listByDataCenterIdAndState(id, ResourceState.Disabled); + public List listEnabledIdsByDataCenterId(long id) { + return listIdsForRoutingByZoneIdAndResourceState(id, ResourceState.Enabled); } - private SearchCriteria scHostsFromZoneUpRouting(long id) { - SearchCriteria sc = DcSearch.create(); - sc.setParameters("dc", id); - sc.setParameters("status", Status.Up); - sc.setParameters("type", Host.Type.Routing); - return sc; + @Override + public List listDisabledIdsByDataCenterId(long id) { + return listIdsForRoutingByZoneIdAndResourceState(id, ResourceState.Disabled); } @Override @@ -590,9 +653,7 @@ private void resetHosts(long managementServerId, long lastPingSecondsAfter) { sb.append(" "); } - if (logger.isTraceEnabled()) { - logger.trace("Following hosts got reset: " + sb.toString()); - } + logger.trace("Following hosts got reset: {}", sb); } /* @@ -602,8 +663,7 @@ private List findClustersOwnedByManagementServer(long managementServerId) SearchCriteria sc = ClustersOwnedByMSSearch.create(); sc.setParameters("server", managementServerId); - List clusters = customSearch(sc, null); - return clusters; + return customSearch(sc, null); } /* @@ -613,13 +673,11 @@ private List findClustersForHostsNotOwnedByAnyManagementServer() { SearchCriteria sc = ClustersForHostsNotOwnedByAnyMSSearch.create(); sc.setJoinParameters("ClusterManagedSearch", "managed", Managed.ManagedState.Managed); - List clusters = customSearch(sc, null); - return clusters; + return customSearch(sc, null); } /** * This determines if hosts belonging to cluster(@clusterId) are up for grabs - * * This is used for handling following cases: * 1. First host added in cluster * 2. During MS restart all hosts in a cluster are without any MS @@ -629,9 +687,7 @@ private boolean canOwnCluster(long clusterId) { sc.setParameters("cluster", clusterId); List hosts = search(sc, null); - boolean ownCluster = (hosts == null || hosts.size() == 0); - - return ownCluster; + return (hosts == null || hosts.isEmpty()); } @Override @@ -648,14 +704,14 @@ public List findAndUpdateDirectAgentToLoad(long lastPingSecondsAfter, Lo logger.debug("Completed resetting hosts suitable for reconnect"); } - List assignedHosts = new ArrayList(); + List assignedHosts = new ArrayList<>(); if (logger.isDebugEnabled()) { logger.debug("Acquiring hosts for clusters already owned by this management server"); } List clusters = findClustersOwnedByManagementServer(managementServerId); txn.start(); - if (clusters.size() > 0) { + if (!clusters.isEmpty()) { // handle clusters already owned by @managementServerId SearchCriteria sc = UnmanagedDirectConnectSearch.create(); sc.setParameters("lastPinged", lastPingSecondsAfter); @@ -670,13 +726,9 @@ public List findAndUpdateDirectAgentToLoad(long lastPingSecondsAfter, Lo sb.append(host.getId()); sb.append(" "); } - if (logger.isTraceEnabled()) { - logger.trace("Following hosts got acquired for clusters already owned: " + sb.toString()); - } - } - if (logger.isDebugEnabled()) { - logger.debug("Completed acquiring hosts for clusters already owned by this management server"); + logger.trace("Following hosts got acquired for clusters already owned: {}", sb); } + logger.debug("Completed acquiring hosts for clusters already owned by this management server"); if (assignedHosts.size() < limit) { if (logger.isDebugEnabled()) { @@ -688,7 +740,7 @@ public List findAndUpdateDirectAgentToLoad(long lastPingSecondsAfter, Lo if (clusters.size() > limit) { updatedClusters = clusters.subList(0, limit.intValue()); } - if (updatedClusters.size() > 0) { + if (!updatedClusters.isEmpty()) { SearchCriteria sc = UnmanagedDirectConnectSearch.create(); sc.setParameters("lastPinged", lastPingSecondsAfter); sc.setJoinParameters("ClusterManagedSearch", "managed", Managed.ManagedState.Managed); @@ -696,10 +748,10 @@ public List findAndUpdateDirectAgentToLoad(long lastPingSecondsAfter, Lo List unmanagedHosts = lockRows(sc, null, true); // group hosts based on cluster - Map> hostMap = new HashMap>(); + Map> hostMap = new HashMap<>(); for (HostVO host : unmanagedHosts) { if (hostMap.get(host.getClusterId()) == null) { - hostMap.put(host.getClusterId(), new ArrayList()); + hostMap.put(host.getClusterId(), new ArrayList<>()); } hostMap.get(host.getClusterId()).add(host); } @@ -720,13 +772,9 @@ public List findAndUpdateDirectAgentToLoad(long lastPingSecondsAfter, Lo break; } } - if (logger.isTraceEnabled()) { - logger.trace("Following hosts got acquired from newly owned clusters: " + sb.toString()); - } - } - if (logger.isDebugEnabled()) { - logger.debug("Completed acquiring hosts for clusters not owned by any management server"); + logger.trace("Following hosts got acquired from newly owned clusters: {}", sb); } + logger.debug("Completed acquiring hosts for clusters not owned by any management server"); } txn.commit(); @@ -780,7 +828,16 @@ public void markHostsAsDisconnected(long msId, long lastPing) { } @Override - public List listByHostTag(Host.Type type, Long clusterId, Long podId, long dcId, String hostTag) { + public List listByHostTag(Host.Type type, Long clusterId, Long podId, Long dcId, String hostTag) { + return listHostsWithOrWithoutHostTags(type, clusterId, podId, dcId, hostTag, true); + } + + private List listHostsWithOrWithoutHostTags(Host.Type type, Long clusterId, Long podId, Long dcId, String hostTags, boolean withHostTags) { + if (StringUtils.isEmpty(hostTags)) { + logger.debug("Host tags not specified, to list hosts"); + return new ArrayList<>(); + } + SearchBuilder hostSearch = createSearchBuilder(); HostVO entity = hostSearch.entity(); hostSearch.and("type", entity.getType(), SearchCriteria.Op.EQ); @@ -791,38 +848,53 @@ public List listByHostTag(Host.Type type, Long clusterId, Long podId, lo hostSearch.and("resourceState", entity.getResourceState(), SearchCriteria.Op.EQ); SearchCriteria sc = hostSearch.create(); - sc.setParameters("type", type.toString()); + if (type != null) { + sc.setParameters("type", type.toString()); + } if (podId != null) { sc.setParameters("pod", podId); } if (clusterId != null) { sc.setParameters("cluster", clusterId); } - sc.setParameters("dc", dcId); + if (dcId != null) { + sc.setParameters("dc", dcId); + } sc.setParameters("status", Status.Up.toString()); sc.setParameters("resourceState", ResourceState.Enabled.toString()); - List tmpHosts = listBy(sc); - List correctHostsByHostTags = new ArrayList(); - List hostIdsByComputeOffTags = findHostByComputeOfferings(hostTag); + List upAndEnabledHosts = listBy(sc); + if (CollectionUtils.isEmpty(upAndEnabledHosts)) { + return new ArrayList<>(); + } - tmpHosts.forEach((host) -> { if(hostIdsByComputeOffTags.contains(host.getId())) correctHostsByHostTags.add(host);}); + List hostIdsByHostTags = findHostIdsByHostTags(hostTags); + if (CollectionUtils.isEmpty(hostIdsByHostTags)) { + return withHostTags ? new ArrayList<>() : upAndEnabledHosts; + } - return correctHostsByHostTags; + if (withHostTags) { + List upAndEnabledHostsWithHostTags = new ArrayList<>(); + upAndEnabledHosts.forEach((host) -> { if (hostIdsByHostTags.contains(host.getId())) upAndEnabledHostsWithHostTags.add(host);}); + return upAndEnabledHostsWithHostTags; + } else { + List upAndEnabledHostsWithoutHostTags = new ArrayList<>(); + upAndEnabledHosts.forEach((host) -> { if (!hostIdsByHostTags.contains(host.getId())) upAndEnabledHostsWithoutHostTags.add(host);}); + return upAndEnabledHostsWithoutHostTags; + } } @Override public List listAllUpAndEnabledNonHAHosts(Type type, Long clusterId, Long podId, long dcId, String haTag) { + if (StringUtils.isNotEmpty(haTag)) { + return listHostsWithOrWithoutHostTags(type, clusterId, podId, dcId, haTag, false); + } + SearchBuilder hostTagSearch = _hostTagsDao.createSearchBuilder(); hostTagSearch.and(); hostTagSearch.op("isTagARule", hostTagSearch.entity().getIsTagARule(), Op.EQ); hostTagSearch.or("tagDoesNotExist", hostTagSearch.entity().getIsTagARule(), Op.NULL); hostTagSearch.cp(); - if (haTag != null && !haTag.isEmpty()) { - hostTagSearch.and().op("tag", hostTagSearch.entity().getTag(), SearchCriteria.Op.NEQ); - hostTagSearch.or("tagNull", hostTagSearch.entity().getTag(), SearchCriteria.Op.NULL); - hostTagSearch.cp(); - } SearchBuilder hostSearch = createSearchBuilder(); @@ -833,18 +905,12 @@ public List listAllUpAndEnabledNonHAHosts(Type type, Long clusterId, Lon hostSearch.and("status", hostSearch.entity().getStatus(), SearchCriteria.Op.EQ); hostSearch.and("resourceState", hostSearch.entity().getResourceState(), SearchCriteria.Op.EQ); - hostSearch.join("hostTagSearch", hostTagSearch, hostSearch.entity().getId(), hostTagSearch.entity().getHostId(), JoinBuilder.JoinType.LEFTOUTER); - SearchCriteria sc = hostSearch.create(); sc.setJoinParameters("hostTagSearch", "isTagARule", false); - if (haTag != null && !haTag.isEmpty()) { - sc.setJoinParameters("hostTagSearch", "tag", haTag); - } - if (type != null) { sc.setParameters("type", type); } @@ -884,12 +950,12 @@ public void loadHostTags(HostVO host) { @DB @Override public List findLostHosts(long timeout) { - List result = new ArrayList(); + List result = new ArrayList<>(); String sql = "select h.id from host h left join cluster c on h.cluster_id=c.id where h.mgmt_server_id is not null and h.last_ping < ? and h.status in ('Up', 'Updating', 'Disconnected', 'Connecting') and h.type not in ('ExternalFirewall', 'ExternalLoadBalancer', 'TrafficMonitor', 'SecondaryStorage', 'LocalSecondaryStorage', 'L2Networking') and (h.cluster_id is null or c.managed_state = 'Managed') ;"; try (TransactionLegacy txn = TransactionLegacy.currentTxn(); - PreparedStatement pstmt = txn.prepareStatement(sql);) { + PreparedStatement pstmt = txn.prepareStatement(sql)) { pstmt.setLong(1, timeout); - try (ResultSet rs = pstmt.executeQuery();) { + try (ResultSet rs = pstmt.executeQuery()) { while (rs.next()) { long id = rs.getLong(1); //ID column result.add(findById(id)); @@ -922,7 +988,7 @@ protected void saveGpuRecords(HostVO host) { HashMap> groupDetails = host.getGpuGroupDetails(); if (groupDetails != null) { // Create/Update GPU group entries - _hostGpuGroupsDao.persist(host.getId(), new ArrayList(groupDetails.keySet())); + _hostGpuGroupsDao.persist(host.getId(), new ArrayList<>(groupDetails.keySet())); // Create/Update VGPU types entries _vgpuTypesDao.persist(host.getId(), groupDetails); } @@ -965,7 +1031,7 @@ public boolean update(Long hostId, HostVO host) { boolean persisted = super.update(hostId, host); if (!persisted) { - return persisted; + return false; } saveDetails(host); @@ -974,7 +1040,7 @@ public boolean update(Long hostId, HostVO host) { txn.commit(); - return persisted; + return true; } @Override @@ -985,11 +1051,10 @@ public List getRunningHostCounts(Date cutTime) { + "select h.data_center_id, h.type, count(*) as count from host as h INNER JOIN mshost as m ON h.mgmt_server_id=m.msid " + "where h.status='Up' and h.type='Routing' and m.last_update > ? " + "group by h.data_center_id, h.type) as t " + "ORDER by t.data_center_id, t.type"; - ArrayList l = new ArrayList(); + ArrayList l = new ArrayList<>(); TransactionLegacy txn = TransactionLegacy.currentTxn(); - ; - PreparedStatement pstmt = null; + PreparedStatement pstmt; try { pstmt = txn.prepareAutoCloseStatement(sql); String gmtCutTime = DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), cutTime); @@ -1013,9 +1078,7 @@ public List getRunningHostCounts(Date cutTime) { @Override public long getNextSequence(long hostId) { - if (logger.isTraceEnabled()) { - logger.trace("getNextSequence(), hostId: " + hostId); - } + logger.trace("getNextSequence(), hostId: {}", hostId); TableGenerator tg = _tgs.get("host_req_sq"); assert tg != null : "how can this be wrong!"; @@ -1084,31 +1147,30 @@ public boolean updateState(Status oldStatus, Event event, Status newStatus, Host HostVO ho = findById(host.getId()); assert ho != null : "How how how? : " + host.getId(); + // TODO handle this if(debug){}else{log.debug} it makes no sense if (logger.isDebugEnabled()) { - - StringBuilder str = new StringBuilder("Unable to update host for event:").append(event.toString()); - str.append(". Name=").append(host.getName()); - str.append("; New=[status=").append(newStatus.toString()).append(":msid=").append(newStatus.lostConnection() ? "null" : host.getManagementServerId()) - .append(":lastpinged=").append(host.getLastPinged()).append("]"); - str.append("; Old=[status=").append(oldStatus.toString()).append(":msid=").append(host.getManagementServerId()).append(":lastpinged=").append(oldPingTime) - .append("]"); - str.append("; DB=[status=").append(vo.getStatus().toString()).append(":msid=").append(vo.getManagementServerId()).append(":lastpinged=").append(vo.getLastPinged()) - .append(":old update count=").append(oldUpdateCount).append("]"); - logger.debug(str.toString()); + String str = "Unable to update host for event:" + event + + ". Name=" + host.getName() + + "; New=[status=" + newStatus + ":msid=" + (newStatus.lostConnection() ? "null" : host.getManagementServerId()) + + ":lastpinged=" + host.getLastPinged() + "]" + + "; Old=[status=" + oldStatus.toString() + ":msid=" + host.getManagementServerId() + ":lastpinged=" + oldPingTime + + "]" + + "; DB=[status=" + vo.getStatus().toString() + ":msid=" + vo.getManagementServerId() + ":lastpinged=" + vo.getLastPinged() + + ":old update count=" + oldUpdateCount + "]"; + logger.debug(str); } else { - StringBuilder msg = new StringBuilder("Agent status update: ["); - msg.append("id = " + host.getId()); - msg.append("; name = " + host.getName()); - msg.append("; old status = " + oldStatus); - msg.append("; event = " + event); - msg.append("; new status = " + newStatus); - msg.append("; old update count = " + oldUpdateCount); - msg.append("; new update count = " + newUpdateCount + "]"); - logger.debug(msg.toString()); + String msg = "Agent status update: [" + "id = " + host.getId() + + "; name = " + host.getName() + + "; old status = " + oldStatus + + "; event = " + event + + "; new status = " + newStatus + + "; old update count = " + oldUpdateCount + + "; new update count = " + newUpdateCount + "]"; + logger.debug(msg); } if (ho.getState() == newStatus) { - logger.debug("Host " + ho.getName() + " state has already been updated to " + newStatus); + logger.debug("Host {} state has already been updated to {}", ho.getName(), newStatus); return true; } } @@ -1134,25 +1196,24 @@ public boolean updateResourceState(ResourceState oldState, ResourceState.Event e int result = update(ub, sc, null); assert result <= 1 : "How can this update " + result + " rows? "; + // TODO handle this if(debug){}else{log.debug} it makes no sense if (logger.isDebugEnabled() && result == 0) { HostVO ho = findById(host.getId()); assert ho != null : "How how how? : " + host.getId(); - StringBuilder str = new StringBuilder("Unable to update resource state: ["); - str.append("m = " + host.getId()); - str.append("; name = " + host.getName()); - str.append("; old state = " + oldState); - str.append("; event = " + event); - str.append("; new state = " + newState + "]"); - logger.debug(str.toString()); + String str = "Unable to update resource state: [" + "m = " + host.getId() + + "; name = " + host.getName() + + "; old state = " + oldState + + "; event = " + event + + "; new state = " + newState + "]"; + logger.debug(str); } else { - StringBuilder msg = new StringBuilder("Resource state update: ["); - msg.append("id = " + host.getId()); - msg.append("; name = " + host.getName()); - msg.append("; old state = " + oldState); - msg.append("; event = " + event); - msg.append("; new state = " + newState + "]"); - logger.debug(msg.toString()); + String msg = "Resource state update: [" + "id = " + host.getId() + + "; name = " + host.getName() + + "; old state = " + oldState + + "; event = " + event + + "; new state = " + newState + "]"; + logger.debug(msg); } return result > 0; @@ -1175,20 +1236,103 @@ public List findByDataCenterId(Long zoneId) { return listBy(sc); } + @Override + public List listIdsByDataCenterId(Long zoneId) { + return listIdsBy(Type.Routing, null, null, null, zoneId, null, null); + } + @Override public List findByPodId(Long podId) { + return findByPodId(podId, null); + } + + @Override + public List findByPodId(Long podId, Type type) { SearchCriteria sc = PodSearch.create(); sc.setParameters("podId", podId); + if (type != null) { + sc.setParameters("type", Type.Routing); + } return listBy(sc); } + @Override + public List listIdsByPodId(Long podId) { + return listIdsBy(null, null, null, null, null, podId, null); + } + @Override public List findByClusterId(Long clusterId) { + return findByClusterId(clusterId, null); + } + + @Override + public List findByClusterId(Long clusterId, Type type) { SearchCriteria sc = ClusterSearch.create(); sc.setParameters("clusterId", clusterId); + if (type != null) { + sc.setParameters("type", Type.Routing); + } return listBy(sc); } + protected List listIdsBy(Host.Type type, Status status, ResourceState resourceState, + HypervisorType hypervisorType, Long zoneId, Long podId, Long clusterId) { + GenericSearchBuilder sb = createSearchBuilder(Long.class); + sb.selectFields(sb.entity().getId()); + sb.and("type", sb.entity().getType(), SearchCriteria.Op.EQ); + sb.and("status", sb.entity().getStatus(), SearchCriteria.Op.EQ); + sb.and("resourceState", sb.entity().getResourceState(), SearchCriteria.Op.EQ); + sb.and("hypervisorType", sb.entity().getHypervisorType(), SearchCriteria.Op.EQ); + sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); + sb.and("podId", sb.entity().getPodId(), SearchCriteria.Op.EQ); + sb.and("clusterId", sb.entity().getClusterId(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + if (type != null) { + sc.setParameters("type", type); + } + if (status != null) { + sc.setParameters("status", status); + } + if (resourceState != null) { + sc.setParameters("resourceState", resourceState); + } + if (hypervisorType != null) { + sc.setParameters("hypervisorType", hypervisorType); + } + if (zoneId != null) { + sc.setParameters("zoneId", zoneId); + } + if (podId != null) { + sc.setParameters("podId", podId); + } + if (clusterId != null) { + sc.setParameters("clusterId", clusterId); + } + return customSearch(sc, null); + } + + @Override + public List listIdsByClusterId(Long clusterId) { + return listIdsBy(null, null, null, null, null, null, clusterId); + } + + @Override + public List listIdsForUpRouting(Long zoneId, Long podId, Long clusterId) { + return listIdsBy(Type.Routing, Status.Up, null, null, zoneId, podId, clusterId); + } + + @Override + public List listIdsByType(Type type) { + return listIdsBy(type, null, null, null, null, null, null); + } + + @Override + public List listIdsForUpEnabledByZoneAndHypervisor(Long zoneId, HypervisorType hypervisorType) { + return listIdsBy(null, Status.Up, ResourceState.Enabled, hypervisorType, zoneId, null, null); + } + @Override public List findByClusterIdAndEncryptionSupport(Long clusterId) { SearchBuilder hostCapabilitySearch = _detailsDao.createSearchBuilder(); @@ -1232,7 +1376,7 @@ public HostVO findByIp(final String ipAddress) { @Override public List findHypervisorHostInCluster(long clusterId) { - SearchCriteria sc = TypeClusterStatusSearch.create(); + SearchCriteria sc = TypeStatusStateSearch.create(); sc.setParameters("type", Host.Type.Routing); sc.setParameters("cluster", clusterId); sc.setParameters("status", Status.Up); @@ -1241,9 +1385,40 @@ public List findHypervisorHostInCluster(long clusterId) { return listBy(sc); } + @Override + public List findHypervisorHostInZone(long zoneId) { + SearchCriteria sc = TypeStatusStateSearch.create(); + sc.setParameters("type", Host.Type.Routing); + sc.setParameters("zone", zoneId); + sc.setParameters("status", Status.Up); + sc.setParameters("resourceState", ResourceState.Enabled); + + return listBy(sc); + } + + @Override + public List findHypervisorHostInPod(long podId) { + SearchCriteria sc = TypeStatusStateSearch.create(); + sc.setParameters("type", Host.Type.Routing); + sc.setParameters("pod", podId); + sc.setParameters("status", Status.Up); + sc.setParameters("resourceState", ResourceState.Enabled); + + return listBy(sc); + } + + @Override + public HostVO findAnyStateHypervisorHostInCluster(long clusterId) { + SearchCriteria sc = TypeStatusStateSearch.create(); + sc.setParameters("type", Host.Type.Routing); + sc.setParameters("cluster", clusterId); + List list = listBy(sc, new Filter(1, true)); + return list.isEmpty() ? null : list.get(0); + } + @Override public HostVO findOldestExistentHypervisorHostInCluster(long clusterId) { - SearchCriteria sc = TypeClusterStatusSearch.create(); + SearchCriteria sc = TypeStatusStateSearch.create(); sc.setParameters("type", Host.Type.Routing); sc.setParameters("cluster", clusterId); sc.setParameters("status", Status.Up); @@ -1251,7 +1426,7 @@ public HostVO findOldestExistentHypervisorHostInCluster(long clusterId) { Filter orderByFilter = new Filter(HostVO.class, "created", true, null, null); List hosts = search(sc, orderByFilter, null, false); - if (hosts != null && hosts.size() > 0) { + if (hosts != null && !hosts.isEmpty()) { return hosts.get(0); } @@ -1260,9 +1435,7 @@ public HostVO findOldestExistentHypervisorHostInCluster(long clusterId) { @Override public List listAllHosts(long zoneId) { - SearchCriteria sc = HostIdSearch.create(); - sc.addAnd("dataCenterId", SearchCriteria.Op.EQ, zoneId); - return customSearch(sc, null); + return listIdsBy(null, null, null, null, zoneId, null, null); } @Override @@ -1296,19 +1469,19 @@ public List listAllHostsThatHaveNoRuleTag(Type type, Long clusterId, Lon } @Override - public List listClustersByHostTag(String computeOfferingTags) { + public List listClustersByHostTag(String hostTags) { TransactionLegacy txn = TransactionLegacy.currentTxn(); - String sql = this.LIST_CLUSTERID_FOR_HOST_TAG; - PreparedStatement pstmt = null; - List result = new ArrayList(); - List tags = Arrays.asList(computeOfferingTags.split(this.SEPARATOR)); - String subselect = getHostIdsByComputeTags(tags); - sql = String.format(sql, subselect); + String selectStmtToListClusterIdsByHostTags = LIST_CLUSTER_IDS_FOR_HOST_TAGS; + PreparedStatement pstmt; + List result = new ArrayList<>(); + List tags = Arrays.asList(hostTags.split(SEPARATOR)); + String selectStmtToListHostIdsByHostTags = getSelectStmtToListHostIdsByHostTags(tags); + selectStmtToListClusterIdsByHostTags = String.format(selectStmtToListClusterIdsByHostTags, selectStmtToListHostIdsByHostTags); try { - pstmt = txn.prepareStatement(sql); + pstmt = txn.prepareStatement(selectStmtToListClusterIdsByHostTags); - for(int i = 0; i < tags.size(); i++){ + for (int i = 0; i < tags.size(); i++){ pstmt.setString(i+1, tags.get(i)); } @@ -1319,20 +1492,20 @@ public List listClustersByHostTag(String computeOfferingTags) { pstmt.close(); return result; } catch (SQLException e) { - throw new CloudRuntimeException("DB Exception on: " + sql, e); + throw new CloudRuntimeException("DB Exception on: " + selectStmtToListClusterIdsByHostTags, e); } } - private List findHostByComputeOfferings(String computeOfferingTags){ + private List findHostIdsByHostTags(String hostTags){ TransactionLegacy txn = TransactionLegacy.currentTxn(); - PreparedStatement pstmt = null; - List result = new ArrayList(); - List tags = Arrays.asList(computeOfferingTags.split(this.SEPARATOR)); - String select = getHostIdsByComputeTags(tags); + PreparedStatement pstmt; + List result = new ArrayList<>(); + List tags = Arrays.asList(hostTags.split(SEPARATOR)); + String selectStmtToListHostIdsByHostTags = getSelectStmtToListHostIdsByHostTags(tags); try { - pstmt = txn.prepareStatement(select); + pstmt = txn.prepareStatement(selectStmtToListHostIdsByHostTags); - for(int i = 0; i < tags.size(); i++){ + for (int i = 0; i < tags.size(); i++){ pstmt.setString(i+1, tags.get(i)); } @@ -1343,7 +1516,7 @@ private List findHostByComputeOfferings(String computeOfferingTags){ pstmt.close(); return result; } catch (SQLException e) { - throw new CloudRuntimeException("DB Exception on: " + select, e); + throw new CloudRuntimeException("DB Exception on: " + selectStmtToListHostIdsByHostTags, e); } } @@ -1368,16 +1541,41 @@ public List findClustersThatMatchHostTagRule(String computeOfferingTags) { return new ArrayList<>(result); } - private String getHostIdsByComputeTags(List offeringTags){ - List questionMarks = new ArrayList(); - offeringTags.forEach((tag) -> { questionMarks.add("?"); }); - return String.format(this.LIST_HOST_IDS_BY_COMPUTETAGS, String.join(",", questionMarks),questionMarks.size()); + @Override + public List listSsvmHostsWithPendingMigrateJobsOrderedByJobCount() { + String query = "SELECT cel.host_id, COUNT(*) " + + "FROM cmd_exec_log cel " + + "JOIN host h ON cel.host_id = h.id " + + "WHERE h.removed IS NULL " + + "GROUP BY cel.host_id " + + "ORDER BY 2"; + + TransactionLegacy txn = TransactionLegacy.currentTxn(); + List result = new ArrayList<>(); + + PreparedStatement pstmt; + try { + pstmt = txn.prepareAutoCloseStatement(query); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + result.add((long) rs.getInt(1)); + } + } catch (SQLException e) { + logger.warn("SQLException caught while listing SSVMs with least migrate jobs.", e); + } + return result; + } + + private String getSelectStmtToListHostIdsByHostTags(List hostTags){ + List questionMarks = new ArrayList<>(); + hostTags.forEach((tag) -> questionMarks.add("?")); + return String.format(LIST_HOST_IDS_BY_HOST_TAGS, String.join(SEPARATOR, questionMarks), questionMarks.size()); } @Override public List listHostsWithActiveVMs(long offeringId) { TransactionLegacy txn = TransactionLegacy.currentTxn(); - PreparedStatement pstmt = null; + PreparedStatement pstmt; List result = new ArrayList<>(); StringBuilder sql = new StringBuilder(GET_HOSTS_OF_ACTIVE_VMS); try { @@ -1396,15 +1594,77 @@ public List listHostsWithActiveVMs(long offeringId) { } @Override - public int countByMs(long msid) { - SearchCriteria sc = ResponsibleMsCountSearch.create(); - sc.setParameters("managementServerId", msid); - return getCount(sc); + public List listHostsByMsAndDc(long msId, long dcId) { + SearchCriteria sc = ResponsibleMsDcSearch.create(); + sc.setParameters("managementServerId", msId); + sc.setParameters("dcId", dcId); + return listBy(sc); + } + + @Override + public List listHostsByMsDcResourceState(long msId, long dcId, List excludedResourceStates) { + QueryBuilder sc = QueryBuilder.create(HostVO.class); + sc.and(sc.entity().getManagementServerId(), Op.EQ, msId); + sc.and(sc.entity().getDataCenterId(), Op.EQ, dcId); + if (CollectionUtils.isNotEmpty(excludedResourceStates)) { + sc.and(sc.entity().getResourceState(), Op.NIN, excludedResourceStates.toArray()); + } + return listBy(sc.create()); + } + + @Override + public List listHostsByMs(long msId) { + SearchCriteria sc = ResponsibleMsSearch.create(); + sc.setParameters("managementServerId", msId); + return listBy(sc); + } + + @Override + public List listHostsByMsResourceState(long msId, List excludedResourceStates) { + QueryBuilder sc = QueryBuilder.create(HostVO.class); + sc.and(sc.entity().getManagementServerId(), Op.EQ, msId); + if (CollectionUtils.isNotEmpty(excludedResourceStates)) { + sc.and(sc.entity().getResourceState(), Op.NIN, excludedResourceStates.toArray()); + } + return listBy(sc.create()); + } + + @Override + public int countHostsByMsResourceStateTypeAndHypervisorType(long msId, + List excludedResourceStates, + List hostTypes, + List hypervisorTypes) { + QueryBuilder sc = QueryBuilder.create(HostVO.class); + sc.and(sc.entity().getManagementServerId(), Op.EQ, msId); + if (CollectionUtils.isNotEmpty(excludedResourceStates)) { + sc.and(sc.entity().getResourceState(), Op.NIN, excludedResourceStates.toArray()); + } + if (CollectionUtils.isNotEmpty(hostTypes)) { + sc.and(sc.entity().getType(), Op.IN, hostTypes.toArray()); + } + if (CollectionUtils.isNotEmpty(hypervisorTypes)) { + sc.and(sc.entity().getHypervisorType(), Op.IN, hypervisorTypes.toArray()); + } + return getCount(sc.create()); + } + + @Override + public List listByMs(long msId) { + SearchCriteria sc = ResponsibleMsIdSearch.create(); + sc.addAnd("managementServerId", SearchCriteria.Op.EQ, msId); + return customSearch(sc, null); + } + + @Override + public List listByLastMs(long msId) { + SearchCriteria sc = LastMsIdSearch.create(); + sc.addAnd("lastManagementServerId", SearchCriteria.Op.EQ, msId); + return customSearch(sc, null); } @Override public List listOrderedHostsHypervisorVersionsInDatacenter(long datacenterId, HypervisorType hypervisorType) { - PreparedStatement pstmt = null; + PreparedStatement pstmt; List result = new ArrayList<>(); try { TransactionLegacy txn = TransactionLegacy.currentTxn(); @@ -1421,15 +1681,6 @@ public List listOrderedHostsHypervisorVersionsInDatacenter(long datacent return result; } - @Override - public List listAllHostsByType(Host.Type type) { - SearchCriteria sc = TypeSearch.create(); - sc.setParameters("type", type); - sc.setParameters("resourceState", ResourceState.Enabled); - - return listBy(sc); - } - @Override public List listByType(Host.Type type) { SearchCriteria sc = TypeSearch.create(); @@ -1504,12 +1755,42 @@ public List listByHostCapability(Type type, Long clusterId, Long podId, return listBy(sc); } + @Override + public List listByClusterHypervisorTypeAndHostCapability(Long clusterId, HypervisorType hypervisorType, String hostCapabilty) { + SearchBuilder hostCapabilitySearch = _detailsDao.createSearchBuilder(); + DetailVO tagEntity = hostCapabilitySearch.entity(); + hostCapabilitySearch.and("capability", tagEntity.getName(), SearchCriteria.Op.EQ); + hostCapabilitySearch.and("value", tagEntity.getValue(), SearchCriteria.Op.EQ); + + SearchBuilder hostSearch = createSearchBuilder(); + HostVO entity = hostSearch.entity(); + hostSearch.and("clusterId", entity.getClusterId(), SearchCriteria.Op.EQ); + hostSearch.and("hypervisor", entity.getHypervisorType(), SearchCriteria.Op.EQ); + hostSearch.and("type", entity.getType(), SearchCriteria.Op.EQ); + hostSearch.and("status", entity.getStatus(), SearchCriteria.Op.EQ); + hostSearch.and("resourceState", entity.getResourceState(), SearchCriteria.Op.EQ); + hostSearch.join("hostCapabilitySearch", hostCapabilitySearch, entity.getId(), tagEntity.getHostId(), JoinBuilder.JoinType.INNER); + + SearchCriteria sc = hostSearch.create(); + sc.setJoinParameters("hostCapabilitySearch", "value", Boolean.toString(true)); + sc.setJoinParameters("hostCapabilitySearch", "capability", hostCapabilty); + + sc.setParameters("clusterId", clusterId); + sc.setParameters("hypervisor", hypervisorType); + sc.setParameters("type", Type.Routing); + sc.setParameters("status", Status.Up); + sc.setParameters("resourceState", ResourceState.Enabled); + return listBy(sc); + } + + @Override public List listByClusterAndHypervisorType(long clusterId, HypervisorType hypervisorType) { SearchCriteria sc = ClusterHypervisorSearch.create(); sc.setParameters("clusterId", clusterId); sc.setParameters("hypervisor", hypervisorType); sc.setParameters("type", Type.Routing); sc.setParameters("status", Status.Up); + sc.setParameters("resourceState", ResourceState.Enabled); return listBy(sc); } @@ -1544,4 +1825,163 @@ private String createSqlFindHostToExecuteCommand(boolean useDisabledHosts) { } return String.format(sqlFindHostInZoneToExecuteCommand, hostResourceStatus); } + + @Override + public boolean isHostUp(long hostId) { + GenericSearchBuilder sb = createSearchBuilder(Status.class); + sb.and("id", sb.entity().getId(), Op.EQ); + sb.selectFields(sb.entity().getStatus()); + SearchCriteria sc = sb.create(); + sc.setParameters("id", hostId); + List statuses = customSearch(sc, null); + return CollectionUtils.isNotEmpty(statuses) && Status.Up.equals(statuses.get(0)); + } + + @Override + public List findHostIdsByZoneClusterResourceStateTypeAndHypervisorType(final Long zoneId, + final Long clusterId, final Long managementServerId, + final List resourceStates, final List types, + final List hypervisorTypes) { + GenericSearchBuilder sb = createSearchBuilder(Long.class); + sb.selectFields(sb.entity().getId()); + sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); + sb.and("clusterId", sb.entity().getClusterId(), SearchCriteria.Op.EQ); + sb.and("msId", sb.entity().getManagementServerId(), SearchCriteria.Op.EQ); + sb.and("resourceState", sb.entity().getResourceState(), SearchCriteria.Op.IN); + sb.and("type", sb.entity().getType(), SearchCriteria.Op.IN); + if (CollectionUtils.isNotEmpty(hypervisorTypes)) { + sb.and().op(sb.entity().getHypervisorType(), SearchCriteria.Op.NULL); + sb.or("hypervisorTypes", sb.entity().getHypervisorType(), SearchCriteria.Op.IN); + sb.cp(); + } + sb.done(); + SearchCriteria sc = sb.create(); + if (zoneId != null) { + sc.setParameters("zoneId", zoneId); + } + if (clusterId != null) { + sc.setParameters("clusterId", clusterId); + } + if (managementServerId != null) { + sc.setParameters("msId", managementServerId); + } + if (CollectionUtils.isNotEmpty(hypervisorTypes)) { + sc.setParameters("hypervisorTypes", hypervisorTypes.toArray()); + } + sc.setParameters("resourceState", resourceStates.toArray()); + sc.setParameters("type", types.toArray()); + return customSearch(sc, null); + } + + @Override + public List listDistinctHypervisorTypes(final Long zoneId) { + GenericSearchBuilder sb = createSearchBuilder(String.class); + sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); + sb.and("type", sb.entity().getType(), SearchCriteria.Op.EQ); + sb.select(null, Func.DISTINCT, sb.entity().getHypervisorType()); + sb.done(); + SearchCriteria sc = sb.create(); + if (zoneId != null) { + sc.setParameters("zoneId", zoneId); + } + sc.setParameters("type", Type.Routing); + List hypervisorString = customSearch(sc, null); + return hypervisorString.stream().map(HypervisorType::getType).collect(Collectors.toList()); + } + + @Override + public List> listDistinctHypervisorArchTypes(final Long zoneId) { + SearchBuilder sb = createSearchBuilder(); + sb.select(null, Func.DISTINCT_PAIR, sb.entity().getHypervisorType(), sb.entity().getArch()); + sb.and("type", sb.entity().getType(), SearchCriteria.Op.EQ); + sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("type", Type.Routing); + if (zoneId != null) { + sc.setParameters("zoneId", zoneId); + } + final List hosts = search(sc, null); + return hosts.stream() + .map(h -> new Pair<>(h.getHypervisorType(), h.getArch())) + .collect(Collectors.toList()); + } + + @Override + public List listDistinctArchTypes(final Long clusterId) { + GenericSearchBuilder sb = createSearchBuilder(String.class); + sb.and("clusterId", sb.entity().getClusterId(), SearchCriteria.Op.EQ); + sb.and("type", sb.entity().getType(), SearchCriteria.Op.EQ); + sb.select(null, Func.DISTINCT, sb.entity().getArch()); + sb.done(); + SearchCriteria sc = sb.create(); + if (clusterId != null) { + sc.setParameters("clusterId", clusterId); + } + sc.setParameters("type", Type.Routing); + List archStrings = customSearch(sc, null); + return archStrings.stream().map(CPU.CPUArch::fromType).collect(Collectors.toList()); + } + + @Override + public List listByIds(List ids) { + if (CollectionUtils.isEmpty(ids)) { + return new ArrayList<>(); + } + SearchCriteria sc = IdsSearch.create(); + sc.setParameters("id", ids.toArray()); + return search(sc, null); + } + + + @Override + public Long findClusterIdByVolumeInfo(VolumeInfo volumeInfo) { + VirtualMachine virtualMachine = volumeInfo.getAttachedVM(); + if (virtualMachine == null) { + return null; + } + + Long hostId = ObjectUtils.defaultIfNull(virtualMachine.getHostId(), virtualMachine.getLastHostId()); + Host host = findById(hostId); + + if (host == null) { + logger.warn(String.format("VM [%s] has null host on DB, either this VM was never started, or there is some inconsistency on the DB.", virtualMachine.getUuid())); + return null; + } + + return host.getClusterId(); + } + + + @Override + public List listDistinctStorageAccessGroups(String name, String keyword) { + GenericSearchBuilder searchBuilder = createSearchBuilder(String.class); + + searchBuilder.select(null, SearchCriteria.Func.DISTINCT, searchBuilder.entity().getStorageAccessGroups()); + if (name != null) { + searchBuilder.and().op("storageAccessGroupExact", searchBuilder.entity().getStorageAccessGroups(), Op.EQ); + searchBuilder.or("storageAccessGroupPrefix", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + searchBuilder.or("storageAccessGroupSuffix", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + searchBuilder.or("storageAccessGroupMiddle", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + searchBuilder.cp(); + } + if (keyword != null) { + searchBuilder.and("keyword", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + } + searchBuilder.done(); + + SearchCriteria sc = searchBuilder.create(); + if (name != null) { + sc.setParameters("storageAccessGroupExact", name); + sc.setParameters("storageAccessGroupPrefix", name + ",%"); + sc.setParameters("storageAccessGroupSuffix", "%," + name); + sc.setParameters("storageAccessGroupMiddle", "%," + name + ",%"); + } + + if (keyword != null) { + sc.setParameters("keyword", "%" + keyword + "%"); + } + + return customSearch(sc, null); + } } diff --git a/engine/schema/src/main/java/com/cloud/host/dao/HostDetailsDao.java b/engine/schema/src/main/java/com/cloud/host/dao/HostDetailsDao.java index 8dc4efa91f3c..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 @@ -32,4 +32,9 @@ public interface HostDetailsDao extends GenericDao { void deleteDetails(long hostId); 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 9c1340592f93..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 @@ -18,6 +18,7 @@ import java.sql.PreparedStatement; import java.sql.SQLException; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -31,12 +32,14 @@ import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.VmDetailConstants; @Component public class HostDetailsDaoImpl extends GenericDaoBase implements HostDetailsDao { protected final SearchBuilder HostSearch; protected final SearchBuilder DetailSearch; protected final SearchBuilder DetailNameSearch; + protected final SearchBuilder ExternalDetailSearch; public HostDetailsDaoImpl() { HostSearch = createSearchBuilder(); @@ -51,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 @@ -130,4 +138,41 @@ public List findByName(String name) { sc.setParameters("name", 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()) { + return; + } + TransactionLegacy txn = TransactionLegacy.currentTxn(); + txn.start(); + List detailVOs = new ArrayList<>(); + for (Map.Entry entry : details.entrySet()) { + String name = entry.getKey(); + String value = entry.getValue(); + if ("password".equals(entry.getKey())) { + value = DBEncryptionUtil.encrypt(value); + } + detailVOs.add(new DetailVO(hostId, name, value)); + } + SearchCriteria sc = ExternalDetailSearch.create(); + sc.setParameters("hostId", hostId); + sc.setParameters("name", VmDetailConstants.EXTERNAL_DETAIL_PREFIX + "%"); + remove(sc); + for (DetailVO detail : detailVOs) { + persist(detail); + } + txn.commit(); + } } diff --git a/engine/schema/src/main/java/com/cloud/host/dao/HostTagsDao.java b/engine/schema/src/main/java/com/cloud/host/dao/HostTagsDao.java index d134db334035..7a00829fd44e 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 @@ -20,6 +20,7 @@ import com.cloud.host.HostTagVO; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.api.response.HostTagResponse; import org.apache.cloudstack.framework.config.ConfigKey; public interface HostTagsDao extends GenericDao { @@ -35,6 +36,13 @@ public interface HostTagsDao extends GenericDao { void deleteTags(long hostId); + boolean updateImplicitTags(long hostId, List hostTags); + + List getExplicitHostTags(long hostId); + List findHostRuleTags(); + HostTagResponse newHostTagResponse(HostTagVO hostTag); + + List searchByIds(Long... hostTagIds); } 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 65deb1d1c9b0..4aa14a31cfcf 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 @@ -16,10 +16,14 @@ // under the License. package com.cloud.host.dao; +import java.util.ArrayList; import java.util.List; +import org.apache.cloudstack.api.response.HostTagResponse; 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.lang3.StringUtils; import org.springframework.stereotype.Component; import com.cloud.host.HostTagVO; @@ -30,14 +34,23 @@ import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.db.SearchCriteria.Func; +import javax.inject.Inject; + @Component public class HostTagsDaoImpl extends GenericDaoBase implements HostTagsDao, Configurable { protected final SearchBuilder HostSearch; protected final GenericSearchBuilder DistinctImplictTagsSearch; + private final SearchBuilder stSearch; + private final SearchBuilder tagIdsearch; + private final SearchBuilder ImplicitTagsSearch; + + @Inject + private ConfigurationDao _configDao; public HostTagsDaoImpl() { HostSearch = createSearchBuilder(); HostSearch.and("hostId", HostSearch.entity().getHostId(), SearchCriteria.Op.EQ); + HostSearch.and("isImplicit", HostSearch.entity().getIsImplicit(), SearchCriteria.Op.EQ); HostSearch.and("isTagARule", HostSearch.entity().getIsTagARule(), SearchCriteria.Op.EQ); HostSearch.done(); @@ -46,6 +59,19 @@ public HostTagsDaoImpl() { DistinctImplictTagsSearch.and("hostIds", DistinctImplictTagsSearch.entity().getHostId(), SearchCriteria.Op.IN); DistinctImplictTagsSearch.and("implicitTags", DistinctImplictTagsSearch.entity().getTag(), SearchCriteria.Op.IN); DistinctImplictTagsSearch.done(); + + stSearch = createSearchBuilder(); + stSearch.and("idIN", stSearch.entity().getId(), SearchCriteria.Op.IN); + stSearch.done(); + + tagIdsearch = createSearchBuilder(); + tagIdsearch.and("id", tagIdsearch.entity().getId(), SearchCriteria.Op.EQ); + tagIdsearch.done(); + + ImplicitTagsSearch = createSearchBuilder(); + ImplicitTagsSearch.and("hostId", ImplicitTagsSearch.entity().getHostId(), SearchCriteria.Op.EQ); + ImplicitTagsSearch.and("isImplicit", ImplicitTagsSearch.entity().getIsImplicit(), SearchCriteria.Op.EQ); + ImplicitTagsSearch.done(); } @Override @@ -74,6 +100,36 @@ public void deleteTags(long hostId) { txn.commit(); } + @Override + public boolean updateImplicitTags(long hostId, List hostTags) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + txn.start(); + SearchCriteria sc = ImplicitTagsSearch.create(); + sc.setParameters("hostId", hostId); + sc.setParameters("isImplicit", true); + boolean expunged = expunge(sc) > 0; + boolean persisted = false; + for (String tag : hostTags) { + if (StringUtils.isNotBlank(tag)) { + HostTagVO vo = new HostTagVO(hostId, tag.trim()); + vo.setIsImplicit(true); + persist(vo); + persisted = true; + } + } + txn.commit(); + return expunged || persisted; + } + + @Override + public List getExplicitHostTags(long hostId) { + SearchCriteria sc = ImplicitTagsSearch.create(); + sc.setParameters("hostId", hostId); + sc.setParameters("isImplicit", false); + + return search(sc, null); + } + @Override public List findHostRuleTags() { SearchCriteria sc = HostSearch.create(); @@ -89,6 +145,7 @@ public void persist(long hostId, List hostTags, Boolean isTagARule) { txn.start(); SearchCriteria sc = HostSearch.create(); sc.setParameters("hostId", hostId); + sc.setParameters("isImplicit", false); expunge(sc); for (String tag : hostTags) { @@ -110,4 +167,72 @@ public ConfigKey[] getConfigKeys() { public String getConfigComponentName() { return HostTagsDaoImpl.class.getSimpleName(); } + + @Override + public HostTagResponse newHostTagResponse(HostTagVO tag) { + HostTagResponse tagResponse = new HostTagResponse(); + + tagResponse.setName(tag.getTag()); + tagResponse.setHostId(tag.getHostId()); + tagResponse.setImplicit(tag.getIsImplicit()); + + tagResponse.setObjectName("hosttag"); + + return tagResponse; + } + + @Override + public List searchByIds(Long... tagIds) { + String batchCfg = _configDao.getValue("detail.batch.query.size"); + + final int detailsBatchSize = batchCfg != null ? Integer.parseInt(batchCfg) : 2000; + + // query details by batches + List tagList = new ArrayList<>(); + int curr_index = 0; + + if (tagIds.length > detailsBatchSize) { + while ((curr_index + detailsBatchSize) <= tagIds.length) { + Long[] ids = new Long[detailsBatchSize]; + + for (int k = 0, j = curr_index; j < curr_index + detailsBatchSize; j++, k++) { + ids[k] = tagIds[j]; + } + + SearchCriteria sc = stSearch.create(); + + sc.setParameters("idIN", (Object[])ids); + + List vms = searchIncludingRemoved(sc, null, null, false); + + if (vms != null) { + tagList.addAll(vms); + } + + curr_index += detailsBatchSize; + } + } + + if (curr_index < tagIds.length) { + int batch_size = (tagIds.length - curr_index); + // set the ids value + Long[] ids = new Long[batch_size]; + + for (int k = 0, j = curr_index; j < curr_index + batch_size; j++, k++) { + ids[k] = tagIds[j]; + } + + SearchCriteria sc = stSearch.create(); + + sc.setParameters("idIN", (Object[])ids); + + List tags = searchIncludingRemoved(sc, null, null, false); + + if (tags != null) { + tagList.addAll(tags); + } + } + + return tagList; + } } diff --git a/engine/schema/src/main/java/com/cloud/hypervisor/HypervisorCapabilitiesVO.java b/engine/schema/src/main/java/com/cloud/hypervisor/HypervisorCapabilitiesVO.java index 5ab684c1100b..a3b03280fdf6 100644 --- a/engine/schema/src/main/java/com/cloud/hypervisor/HypervisorCapabilitiesVO.java +++ b/engine/schema/src/main/java/com/cloud/hypervisor/HypervisorCapabilitiesVO.java @@ -19,9 +19,8 @@ import java.util.UUID; import javax.persistence.Column; +import javax.persistence.Convert; import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @@ -29,6 +28,7 @@ import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.utils.NumbersUtil; +import org.apache.cloudstack.util.HypervisorTypeConverter; @Entity @Table(name = "hypervisor_capabilities") @@ -39,7 +39,7 @@ public class HypervisorCapabilitiesVO implements HypervisorCapabilities { private long id; @Column(name = "hypervisor_type") - @Enumerated(value = EnumType.STRING) + @Convert(converter = HypervisorTypeConverter.class) private HypervisorType hypervisorType; @Column(name = "hypervisor_version") @@ -80,6 +80,18 @@ public HypervisorCapabilitiesVO(HypervisorType hypervisorType, String hypervisor this.uuid = UUID.randomUUID().toString(); } + public HypervisorCapabilitiesVO(HypervisorCapabilitiesVO source) { + this.hypervisorType = source.getHypervisorType(); + this.hypervisorVersion = source.getHypervisorVersion(); + this.maxGuestsLimit = source.getMaxGuestsLimit(); + this.maxDataVolumesLimit = source.getMaxDataVolumesLimit(); + this.maxHostsPerCluster = source.getMaxHostsPerCluster(); + this.securityGroupEnabled = source.isSecurityGroupEnabled(); + this.storageMotionSupported = source.isStorageMotionSupported(); + this.vmSnapshotEnabled = source.isVmSnapshotEnabled(); + this.uuid = UUID.randomUUID().toString(); + } + /** * @param hypervisorType the hypervisorType to set */ 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 22bb2c26b652..ee5f67b09cd1 100644 --- a/engine/schema/src/main/java/com/cloud/network/LBHealthCheckPolicyVO.java +++ b/engine/schema/src/main/java/com/cloud/network/LBHealthCheckPolicyVO.java @@ -27,6 +27,7 @@ import javax.persistence.Table; import com.cloud.network.rules.HealthCheckPolicy; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "load_balancer_healthcheck_policies") @@ -169,4 +170,11 @@ public void setDisplay(boolean display) { public boolean isDisplay() { return display; } + + @Override + public String toString() { + return String.format("LBHealthCheckPolicy %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "pingPath")); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/as/AutoScalePolicyVO.java b/engine/schema/src/main/java/com/cloud/network/as/AutoScalePolicyVO.java index fa5dcafba341..24d8b8e7f40a 100644 --- a/engine/schema/src/main/java/com/cloud/network/as/AutoScalePolicyVO.java +++ b/engine/schema/src/main/java/com/cloud/network/as/AutoScalePolicyVO.java @@ -33,6 +33,7 @@ import org.apache.cloudstack.api.InternalIdentity; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "autoscale_policies") @@ -92,7 +93,9 @@ public AutoScalePolicyVO(String name, long domainId, long accountId, int duratio @Override public String toString() { - return new StringBuilder("AutoScalePolicy[").append("id-").append(id).append("]").toString(); + return String.format("AutoScalePolicy %s.", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name")); } @Override diff --git a/engine/schema/src/main/java/com/cloud/network/as/AutoScaleVmGroupVO.java b/engine/schema/src/main/java/com/cloud/network/as/AutoScaleVmGroupVO.java index 652cbb340a32..307de9f1a60d 100644 --- a/engine/schema/src/main/java/com/cloud/network/as/AutoScaleVmGroupVO.java +++ b/engine/schema/src/main/java/com/cloud/network/as/AutoScaleVmGroupVO.java @@ -32,6 +32,7 @@ import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import org.apache.commons.lang3.StringUtils; import com.cloud.utils.db.GenericDao; @@ -126,11 +127,9 @@ public AutoScaleVmGroupVO(long lbRuleId, long zoneId, long domainId, @Override public String toString() { - return new StringBuilder("AutoScaleVmGroupVO[").append("id=").append(id) - .append("|name=").append(name) - .append("|loadBalancerId=").append(loadBalancerId) - .append("|profileId=").append(profileId) - .append("]").toString(); + return String.format("AutoScaleVmGroup %s.", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "loadBalancerId", "profileId")); } @Override diff --git a/engine/schema/src/main/java/com/cloud/network/as/AutoScaleVmProfileVO.java b/engine/schema/src/main/java/com/cloud/network/as/AutoScaleVmProfileVO.java index 21291062756c..562d908507e8 100644 --- a/engine/schema/src/main/java/com/cloud/network/as/AutoScaleVmProfileVO.java +++ b/engine/schema/src/main/java/com/cloud/network/as/AutoScaleVmProfileVO.java @@ -37,6 +37,7 @@ import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.StringUtils; @@ -126,7 +127,9 @@ public AutoScaleVmProfileVO(long zoneId, long domainId, long accountId, long ser @Override public String toString() { - return new StringBuilder("AutoScaleVMProfileVO[").append("id").append(id).append("-").append("templateId").append("-").append(templateId).append("]").toString(); + return String.format("AutoScaleVMProfile %s.", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "templateId")); } @Override diff --git a/engine/schema/src/main/java/com/cloud/network/as/ConditionVO.java b/engine/schema/src/main/java/com/cloud/network/as/ConditionVO.java index 18e67a4af61c..0679dac32355 100644 --- a/engine/schema/src/main/java/com/cloud/network/as/ConditionVO.java +++ b/engine/schema/src/main/java/com/cloud/network/as/ConditionVO.java @@ -33,6 +33,7 @@ import org.apache.cloudstack.api.InternalIdentity; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "conditions") @@ -91,7 +92,9 @@ public long getId() { @Override public String toString() { - return new StringBuilder("Condition[").append("id-").append(id).append("]").toString(); + return String.format("Condition %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid")); } @Override diff --git a/engine/schema/src/main/java/com/cloud/network/as/CounterVO.java b/engine/schema/src/main/java/com/cloud/network/as/CounterVO.java index e5ab9886dda7..be21515bb51a 100644 --- a/engine/schema/src/main/java/com/cloud/network/as/CounterVO.java +++ b/engine/schema/src/main/java/com/cloud/network/as/CounterVO.java @@ -34,6 +34,7 @@ import com.cloud.network.Network; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "counter") @@ -79,7 +80,9 @@ public CounterVO(Source source, String name, String value, Network.Provider prov @Override public String toString() { - return new StringBuilder("Counter[").append("id-").append(id).append("]").toString(); + return String.format("Counter %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name")); } @Override diff --git a/engine/schema/src/main/java/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDao.java b/engine/schema/src/main/java/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDao.java index 4b25c63403e2..9775f8ad5b10 100644 --- a/engine/schema/src/main/java/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDao.java +++ b/engine/schema/src/main/java/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDao.java @@ -35,4 +35,8 @@ public interface AutoScaleVmGroupVmMapDao extends GenericDao vmIds, Long batchSize); + + int getErroredInstanceCount(long vmGroupId); } diff --git a/engine/schema/src/main/java/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDaoImpl.java index 8fca4c26f9a7..b2f4e578a82f 100644 --- a/engine/schema/src/main/java/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDaoImpl.java @@ -18,7 +18,10 @@ import java.util.List; +import javax.annotation.PostConstruct; +import javax.inject.Inject; +import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; import com.cloud.network.as.AutoScaleVmGroupVmMapVO; @@ -31,9 +34,6 @@ import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.dao.VMInstanceDao; -import javax.annotation.PostConstruct; -import javax.inject.Inject; - @Component public class AutoScaleVmGroupVmMapDaoImpl extends GenericDaoBase implements AutoScaleVmGroupVmMapDao { @@ -115,4 +115,25 @@ public boolean removeByGroup(long vmGroupId) { sc.setParameters("vmGroupId", vmGroupId); return remove(sc) >= 0; } + + @Override + public int expungeByVmList(List vmIds, Long batchSize) { + if (CollectionUtils.isEmpty(vmIds)) { + return 0; + } + SearchBuilder sb = createSearchBuilder(); + sb.and("vmIds", sb.entity().getInstanceId(), SearchCriteria.Op.IN); + SearchCriteria sc = sb.create(); + sc.setParameters("vmIds", vmIds.toArray()); + return batchExpunge(sc, batchSize); + } + + @Override + public int getErroredInstanceCount(long vmGroupId) { + SearchCriteria sc = CountBy.create(); + sc.setParameters("vmGroupId", vmGroupId); + sc.setJoinParameters("vmSearch", "states", State.Error); + final List results = customSearch(sc, null); + return results.get(0); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/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/ExternalLoadBalancerDeviceVO.java b/engine/schema/src/main/java/com/cloud/network/dao/ExternalLoadBalancerDeviceVO.java index 80bec1b8152f..88c5c0885a87 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/ExternalLoadBalancerDeviceVO.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/ExternalLoadBalancerDeviceVO.java @@ -30,6 +30,7 @@ import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; import org.apache.cloudstack.network.ExternalNetworkDeviceManager; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; /** * ExternalLoadBalancerDeviceVO contains information on external load balancer devices (F5/Netscaler VPX,MPX,SDX) added into a deployment @@ -244,4 +245,11 @@ public String getUuid() { public void setUuid(String uuid) { this.uuid = uuid; } + + @Override + public String toString() { + return String.format("ExternalLoadBalancerDevice %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "providerName")); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesCidrsDao.java b/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesCidrsDao.java index 55c454860efe..df5b6b5d647c 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesCidrsDao.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesCidrsDao.java @@ -29,4 +29,6 @@ public interface FirewallRulesCidrsDao extends GenericDao listByFirewallRuleId(long firewallRuleId); + + void updateSourceCidrsForRule(Long firewallRuleId, List sourceCidrList); } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesCidrsDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesCidrsDaoImpl.java index fdd1e0ec43ad..6279289bdfed 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesCidrsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesCidrsDaoImpl.java @@ -20,6 +20,7 @@ import java.util.List; +import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Component; import com.cloud.utils.db.DB; @@ -45,7 +46,7 @@ public List getSourceCidrs(long firewallRuleId) { sc.setParameters("firewallRuleId", firewallRuleId); List results = search(sc, null); - List cidrs = new ArrayList(results.size()); + List cidrs = new ArrayList<>(results.size()); for (FirewallRulesCidrsVO result : results) { cidrs.add(result.getCidr()); } @@ -63,9 +64,27 @@ public List listByFirewallRuleId(long firewallRuleId) { return results; } + @Override + public void updateSourceCidrsForRule(Long firewallRuleId, List sourceCidrList) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + txn.start(); + + SearchCriteria sc = CidrsSearch.create(); + sc.setParameters("firewallRuleId", firewallRuleId); + remove(sc); + + persist(firewallRuleId, sourceCidrList); + + txn.commit(); + } + @Override @DB public void persist(long firewallRuleId, List sourceCidrs) { + if (CollectionUtils.isEmpty(sourceCidrs)) { + return; + } + TransactionLegacy txn = TransactionLegacy.currentTxn(); txn.start(); 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 21200dbf9b58..7f322ae6c037 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 @@ -43,7 +43,9 @@ public interface FirewallRulesDao extends GenericDao { List listStaticNatByVmId(long vmId); - List listByIpPurposeAndProtocolAndNotRevoked(long ipAddressId, Integer startPort, Integer endPort, String protocol, FirewallRule.Purpose purpose); + List listByIpPurposePortsProtocolAndNotRevoked(long ipAddressId, Integer startPort, Integer endPort, String protocol, FirewallRule.Purpose purpose); + + List listByIpPurposeProtocolAndNotRevoked(long ipAddressId, FirewallRule.Purpose purpose, String protocol); FirewallRuleVO findByRelatedId(long ruleId); @@ -72,4 +74,8 @@ public interface FirewallRulesDao extends GenericDao { void loadSourceCidrs(FirewallRuleVO rule); void loadDestinationCidrs(FirewallRuleVO rule); + + FirewallRuleVO findByNetworkIdAndPorts(long networkId, int startPort, int endPort); + + List listRoutingIngressFirewallRules(long networkId); } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDaoImpl.java index 3ac860b08c5f..27bf7ba6aa83 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDaoImpl.java @@ -48,8 +48,10 @@ public class FirewallRulesDaoImpl extends GenericDaoBase i protected final SearchBuilder NotRevokedSearch; protected final SearchBuilder ReleaseSearch; protected SearchBuilder VmSearch; + protected SearchBuilder FirewallByPortsAndNetwork; protected final SearchBuilder SystemRuleSearch; protected final GenericSearchBuilder RulesByIpCount; + protected final SearchBuilder RoutingFirewallRulesSearch; @Inject protected FirewallRulesCidrsDao _firewallRulesCidrsDao; @@ -104,6 +106,19 @@ protected FirewallRulesDaoImpl() { RulesByIpCount.and("ipAddressId", RulesByIpCount.entity().getSourceIpAddressId(), Op.EQ); RulesByIpCount.and("state", RulesByIpCount.entity().getState(), Op.EQ); RulesByIpCount.done(); + + FirewallByPortsAndNetwork = createSearchBuilder(); + FirewallByPortsAndNetwork.and("networkId", FirewallByPortsAndNetwork.entity().getNetworkId(), Op.EQ); + FirewallByPortsAndNetwork.and("sourcePortStart", FirewallByPortsAndNetwork.entity().getSourcePortStart(), Op.EQ); + FirewallByPortsAndNetwork.and("sourcePortEnd", FirewallByPortsAndNetwork.entity().getSourcePortEnd(), Op.EQ); + FirewallByPortsAndNetwork.done(); + + RoutingFirewallRulesSearch = createSearchBuilder(); + RoutingFirewallRulesSearch.and("networkId", RoutingFirewallRulesSearch.entity().getNetworkId(), Op.EQ); + RoutingFirewallRulesSearch.and("purpose", RoutingFirewallRulesSearch.entity().getPurpose(), Op.EQ); + RoutingFirewallRulesSearch.and("trafficType", RoutingFirewallRulesSearch.entity().getTrafficType(), Op.EQ); + RoutingFirewallRulesSearch.and("ipId", RoutingFirewallRulesSearch.entity().getSourceIpAddressId(), Op.NULL); + RoutingFirewallRulesSearch.done(); } @Override @@ -243,9 +258,6 @@ public FirewallRuleVO persist(FirewallRuleVO firewallRule) { } public void saveSourceCidrs(FirewallRuleVO firewallRule, List cidrList) { - if (cidrList == null) { - return; - } _firewallRulesCidrsDao.persist(firewallRule.getId(), cidrList); } @@ -258,8 +270,25 @@ public void saveDestinationCidrs(FirewallRuleVO firewallRule, List cidrL } @Override - public List listByIpPurposeAndProtocolAndNotRevoked(long ipAddressId, Integer startPort, Integer endPort, String protocol, - FirewallRule.Purpose purpose) { + public List listByIpPurposeProtocolAndNotRevoked(long ipAddressId, Purpose purpose, String protocol) { + SearchCriteria sc = NotRevokedSearch.create(); + sc.setParameters("ipId", ipAddressId); + sc.setParameters("state", State.Revoke); + + if (purpose != null) { + sc.setParameters("purpose", purpose); + } + + if (protocol != null) { + sc.setParameters("protocol", protocol); + } + + return listBy(sc); + } + + @Override + public List listByIpPurposePortsProtocolAndNotRevoked(long ipAddressId, Integer startPort, Integer endPort, String protocol, + FirewallRule.Purpose purpose) { SearchCriteria sc = NotRevokedSearch.create(); sc.setParameters("ipId", ipAddressId); sc.setParameters("state", State.Revoke); @@ -386,4 +415,22 @@ public void loadDestinationCidrs(FirewallRuleVO rule){ rule.setDestinationCidrsList(destCidrs); } + @Override + public FirewallRuleVO findByNetworkIdAndPorts(long networkId, int startPort, int endPort) { + SearchCriteria sc = FirewallByPortsAndNetwork.create(); + sc.setParameters("networkId", networkId); + sc.setParameters("sourcePortStart", startPort); + sc.setParameters("sourcePortEnd", endPort); + + return findOneBy(sc); + } + + @Override + public List listRoutingIngressFirewallRules(long networkId) { + SearchCriteria sc = RoutingFirewallRulesSearch.create(); + sc.setParameters("networkId", networkId); + sc.setParameters("purpose", Purpose.Firewall); + sc.setParameters("trafficType", TrafficType.Ingress); + return listBy(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDao.java b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDao.java index b1b1e1cf7571..3f8c36ac94ed 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDao.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDao.java @@ -105,4 +105,6 @@ public interface IPAddressDao extends GenericDao { void buildQuarantineSearchCriteria(SearchCriteria sc); IPAddressVO findBySourceNetworkIdAndDatacenterIdAndState(long sourceNetworkId, long dataCenterId, State state); + + int expungeByVmList(List vmIds, Long batchSize); } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDaoImpl.java index ca779f7e9cee..0a5ecd25667e 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDaoImpl.java @@ -26,6 +26,7 @@ import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.resourcedetail.dao.UserIpAddressDetailsDao; +import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; import com.cloud.dc.Vlan.VlanType; @@ -196,6 +197,7 @@ public void unassignIpAddress(long ipAddressId) { address.setSourceNat(false); address.setOneToOneNat(false); address.setAssociatedWithVmId(null); + address.setForRouter(false); address.setState(State.Free); address.setAssociatedWithNetworkId(null); address.setVpcId(null); @@ -420,7 +422,7 @@ public long countFreePublicIPs() { public long countFreeIpsInVlan(long vlanDbId) { SearchCriteria sc = VlanDbIdSearchUnallocated.create(); sc.setParameters("vlanDbId", vlanDbId); - return listBy(sc).size(); + return getCount(sc); } @Override @@ -561,4 +563,16 @@ public IPAddressVO findBySourceNetworkIdAndDatacenterIdAndState(long sourceNetwo sc.setParameters("state", State.Free); return findOneBy(sc); } + + @Override + public int expungeByVmList(List vmIds, Long batchSize) { + if (CollectionUtils.isEmpty(vmIds)) { + return 0; + } + SearchBuilder sb = createSearchBuilder(); + sb.and("vmIds", sb.entity().getAssociatedWithVmId(), SearchCriteria.Op.IN); + SearchCriteria sc = sb.create(); + sc.setParameters("vmIds", vmIds.toArray()); + return batchExpunge(sc, batchSize); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressVO.java b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressVO.java index 4c7569a55b96..a3a65fdb01b3 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressVO.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressVO.java @@ -33,6 +33,7 @@ import com.cloud.network.IpAddress; import com.cloud.utils.db.GenericDao; import com.cloud.utils.net.Ip; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; /** * A bean representing a public IP Address @@ -116,6 +117,9 @@ public class IPAddressVO implements IpAddress { @Column(name = "forsystemvms") private boolean forSystemVms = false; + @Column(name = "for_router") + private boolean forRouter = false; + @Column(name= GenericDao.REMOVED_COLUMN) private Date removed; @@ -268,7 +272,9 @@ public void setState(State state) { @Override public String toString() { - return new StringBuilder("Ip[").append(address).append("-").append(dataCenterId).append("]").toString(); + return String.format("IPAddress %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "dataCenterId", "address")); } @Override @@ -385,4 +391,13 @@ public void setRuleState(State ruleState) { public boolean isForSystemVms() { return forSystemVms; } + + @Override + public boolean isForRouter() { + return forRouter; + } + + public void setForRouter(boolean forRouter) { + this.forRouter = forRouter; + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/InlineLoadBalancerNicMapDao.java b/engine/schema/src/main/java/com/cloud/network/dao/InlineLoadBalancerNicMapDao.java index ac3845beffe4..b1831b407a41 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/InlineLoadBalancerNicMapDao.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/InlineLoadBalancerNicMapDao.java @@ -16,10 +16,14 @@ // under the License. package com.cloud.network.dao; +import java.util.List; + import com.cloud.utils.db.GenericDao; public interface InlineLoadBalancerNicMapDao extends GenericDao { InlineLoadBalancerNicMapVO findByPublicIpAddress(String publicIpAddress); InlineLoadBalancerNicMapVO findByNicId(long nicId); + int expungeByNicList(List nicIds, Long batchSize); + } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/InlineLoadBalancerNicMapDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/InlineLoadBalancerNicMapDaoImpl.java index 1c3f231f9c1d..d64ba8b4155f 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/InlineLoadBalancerNicMapDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/InlineLoadBalancerNicMapDaoImpl.java @@ -17,9 +17,13 @@ package com.cloud.network.dao; +import java.util.List; + +import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; @Component @@ -41,4 +45,15 @@ public InlineLoadBalancerNicMapVO findByNicId(long nicId) { return findOneBy(sc); } + @Override + public int expungeByNicList(List nicIds, Long batchSize) { + if (CollectionUtils.isEmpty(nicIds)) { + return 0; + } + SearchBuilder sb = createSearchBuilder(); + sb.and("nicIds", sb.entity().getNicId(), SearchCriteria.Op.IN); + SearchCriteria sc = sb.create(); + sc.setParameters("nicIds", nicIds.toArray()); + return batchExpunge(sc, batchSize); + } } 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 e9f50a75a7b7..72b8fc151b78 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 @@ -33,6 +33,7 @@ import com.cloud.network.rules.StickinessPolicy; import com.cloud.utils.Pair; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "load_balancer_stickiness_policies") @@ -162,4 +163,11 @@ public void setDisplay(boolean display) { public boolean isDisplay() { return display; } + + @Override + public String toString() { + return String.format("LBStickinessPolicy %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "methodName")); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/LoadBalancerVMMapDao.java b/engine/schema/src/main/java/com/cloud/network/dao/LoadBalancerVMMapDao.java index a25534b7010f..be2941d5cb2f 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/LoadBalancerVMMapDao.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/LoadBalancerVMMapDao.java @@ -42,4 +42,5 @@ public interface LoadBalancerVMMapDao extends GenericDao vmIds, Long batchSize); } 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 b32320a84cb0..dc37cdeefe3d 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 @@ -18,11 +18,12 @@ import java.util.List; - +import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; 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.Func; @@ -135,4 +136,16 @@ public List listByLoadBalancerIdAndVmId(long loadBalancerId sc.addAnd("instanceId", SearchCriteria.Op.EQ, instanceId); return listBy(sc); } + + @Override + public int expungeByVmList(List vmIds, Long batchSize) { + if (CollectionUtils.isEmpty(vmIds)) { + return 0; + } + SearchBuilder sb = createSearchBuilder(); + sb.and("vmIds", sb.entity().getInstanceId(), SearchCriteria.Op.IN); + SearchCriteria sc = sb.create(); + sc.setParameters("vmIds", vmIds.toArray()); + return batchExpunge(sc, batchSize); + } } 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 bd5ea95dcc7f..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 @@ -27,6 +27,7 @@ import com.cloud.network.rules.FirewallRuleVO; import com.cloud.network.rules.LoadBalancer; import com.cloud.utils.net.NetUtils; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; /** * This VO represents Public Load Balancer @@ -136,4 +137,20 @@ public Scheme getScheme() { public String getCidrList() { return cidrList; } + + @Override + public String toString() { + return String.format("LoadBalancer %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "purpose", "state")); + } + + /** + * Sets the CIDR list associated with this load balancer rule. + * + * @param cidrList a comma-separated list of CIDR strings, e.g. "1.2.3.4/24,1.2.3.5/24" or an empty string e.g. "" to clear the restrictions + */ + public void setCidrList(String cidrList) { + this.cidrList = cidrList; + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetrisProviderDao.java b/engine/schema/src/main/java/com/cloud/network/dao/NetrisProviderDao.java new file mode 100644 index 000000000000..fe21f72e4dbb --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/network/dao/NetrisProviderDao.java @@ -0,0 +1,24 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.dao; + +import com.cloud.network.element.NetrisProviderVO; +import com.cloud.utils.db.GenericDao; + +public interface NetrisProviderDao extends GenericDao { + NetrisProviderVO findByZoneId(long zoneId); +} diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetrisProviderDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/NetrisProviderDaoImpl.java new file mode 100644 index 000000000000..86ea04f1db09 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/network/dao/NetrisProviderDaoImpl.java @@ -0,0 +1,52 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.dao; + +import com.cloud.network.element.NetrisProviderVO; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.springframework.stereotype.Component; + +@Component +@DB() +public class NetrisProviderDaoImpl extends GenericDaoBase implements NetrisProviderDao { + + final SearchBuilder allFieldsSearch; + + public NetrisProviderDaoImpl() { + super(); + allFieldsSearch = createSearchBuilder(); + allFieldsSearch.and("id", allFieldsSearch.entity().getId(), + SearchCriteria.Op.EQ); + allFieldsSearch.and("uuid", allFieldsSearch.entity().getUuid(), + SearchCriteria.Op.EQ); + allFieldsSearch.and("hostname", allFieldsSearch.entity().getUrl(), + SearchCriteria.Op.EQ); + allFieldsSearch.and("zone_id", allFieldsSearch.entity().getZoneId(), + SearchCriteria.Op.EQ); + allFieldsSearch.done(); + } + + @Override + public NetrisProviderVO findByZoneId(long zoneId) { + SearchCriteria sc = allFieldsSearch.create(); + sc.setParameters("zone_id", zoneId); + return findOneBy(sc); + } +} diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDao.java b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDao.java index 0861a424ebb6..fdca6e43f00f 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,6 +18,7 @@ import java.util.List; import java.util.Map; +import java.util.Set; import com.cloud.network.Network; import com.cloud.network.Network.GuestType; @@ -47,6 +48,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. * 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 fa448b026e45..4e8b6204f720 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 @@ -86,6 +86,7 @@ public class NetworkDaoImpl extends GenericDaoBaseimplements Ne GenericSearchBuilder GarbageCollectedSearch; SearchBuilder PrivateNetworkSearch; + SearchBuilder NetworkDomainSearch; @Inject ResourceTagDao _tagsDao; @@ -198,6 +199,12 @@ protected void init() { 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(); @@ -415,8 +422,7 @@ public int getOtherPersistentNetworksCount(long id, String broadcastURI, boolean sc.setParameters("broadcastUri", broadcastURI); sc.setParameters("guestType", guestTypes); sc.setJoinParameters("persistent", "persistent", isPersistent); - List persistentNetworks = search(sc, null); - return persistentNetworks.size(); + return getCount(sc); } @Override @@ -429,12 +435,35 @@ public List 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); } @@ -569,7 +598,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); } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetworkServiceMapDao.java b/engine/schema/src/main/java/com/cloud/network/dao/NetworkServiceMapDao.java index e0509f80c2a4..1675c89811a5 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/NetworkServiceMapDao.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/NetworkServiceMapDao.java @@ -30,6 +30,8 @@ public interface NetworkServiceMapDao extends GenericDao { boolean areServicesSupportedInNetwork(long networkId, Service... services); + boolean isAnyServiceSupportedInNetwork(long networkId, Provider provider, Service... services); + boolean canProviderSupportServiceInNetwork(long networkId, Service service, Provider provider); List getServicesInNetwork(long networkId); diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetworkServiceMapDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/NetworkServiceMapDaoImpl.java index 31e083075fa9..f25bee5da471 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/NetworkServiceMapDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/NetworkServiceMapDaoImpl.java @@ -90,6 +90,28 @@ public boolean areServicesSupportedInNetwork(long networkId, Service... services return false; } + @Override + public boolean isAnyServiceSupportedInNetwork(long networkId, Provider provider, Service... services) { + SearchCriteria sc = MultipleServicesSearch.create(); + sc.setParameters("networkId", networkId); + sc.setParameters("provider", provider.getName()); + + if (services != null) { + String[] servicesStr = new String[services.length]; + + int i = 0; + for (Service service : services) { + servicesStr[i] = service.getName(); + i++; + } + + sc.setParameters("service", (Object[])servicesStr); + } + + List networkServices = listBy(sc); + return !networkServices.isEmpty(); + } + @Override public boolean canProviderSupportServiceInNetwork(long networkId, Service service, Provider provider) { SearchCriteria sc = AllFieldsSearch.create(); 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 fde93238451a..02abaacd854e 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,6 +203,9 @@ public class NetworkVO implements Network { @Column(name = "private_mtu") Integer privateMtu; + @Transient + Integer networkCidrSize; + public NetworkVO() { uuid = UUID.randomUUID().toString(); } @@ -367,6 +370,10 @@ public Mode getMode() { return mode; } + public void setAccountId(long accountId) { + this.accountId = accountId; + } + @Override public long getAccountId() { return accountId; @@ -440,6 +447,7 @@ public String getGateway() { return gateway; } + @Override public void setGateway(String gateway) { this.gateway = gateway; } @@ -453,6 +461,7 @@ public String getCidr() { return cidr; } + @Override public void setCidr(String cidr) { this.cidr = cidr; } @@ -755,4 +764,13 @@ public Integer getPrivateMtu() { public void setPrivateMtu(Integer privateMtu) { this.privateMtu = privateMtu; } + + @Override + public Integer getNetworkCidrSize() { + return networkCidrSize; + } + + public void setNetworkCidrSize(Integer networkCidrSize) { + this.networkCidrSize = networkCidrSize; + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NsxProviderDao.java b/engine/schema/src/main/java/com/cloud/network/dao/NsxProviderDao.java new file mode 100644 index 000000000000..0fc775377112 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/network/dao/NsxProviderDao.java @@ -0,0 +1,30 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.dao; + +import com.cloud.network.element.NsxProviderVO; +import com.cloud.utils.db.GenericDao; + +import java.util.List; + +public interface NsxProviderDao extends GenericDao { + NsxProviderVO findByZoneId(long zoneId); + + NsxProviderVO findByUuid(String uuid); + + List findAll(); +} diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NsxProviderDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/NsxProviderDaoImpl.java new file mode 100644 index 000000000000..cf7b5d405c00 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/network/dao/NsxProviderDaoImpl.java @@ -0,0 +1,65 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.dao; + +import com.cloud.network.element.NsxProviderVO; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +@DB() +public class NsxProviderDaoImpl extends GenericDaoBase + implements NsxProviderDao { + + final SearchBuilder allFieldsSearch; + + public NsxProviderDaoImpl() { + super(); + allFieldsSearch = createSearchBuilder(); + allFieldsSearch.and("id", allFieldsSearch.entity().getId(), + SearchCriteria.Op.EQ); + allFieldsSearch.and("uuid", allFieldsSearch.entity().getUuid(), + SearchCriteria.Op.EQ); + allFieldsSearch.and("hostname", allFieldsSearch.entity().getHostname(), + SearchCriteria.Op.EQ); + allFieldsSearch.and("provider_name", allFieldsSearch.entity().getProviderName(), + SearchCriteria.Op.EQ); + allFieldsSearch.and("tier0_gateway", allFieldsSearch.entity().getTier0Gateway(), + SearchCriteria.Op.EQ); + allFieldsSearch.and("zone_id", allFieldsSearch.entity().getZoneId(), + SearchCriteria.Op.EQ); + allFieldsSearch.and("edge_cluster", allFieldsSearch.entity().getEdgeCluster(), + SearchCriteria.Op.EQ); + allFieldsSearch.done(); + } + @Override + public NsxProviderVO findByZoneId(long zoneId) { + SearchCriteria sc = allFieldsSearch.create(); + sc.setParameters("zone_id", zoneId); + return findOneBy(sc); + } + + @Override + public List findAll() { + return listAll(); + } +} diff --git a/engine/schema/src/main/java/com/cloud/network/dao/OpRouterMonitorServiceDao.java b/engine/schema/src/main/java/com/cloud/network/dao/OpRouterMonitorServiceDao.java index ebc0f1af2271..0516e26e13a7 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/OpRouterMonitorServiceDao.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/OpRouterMonitorServiceDao.java @@ -18,8 +18,12 @@ package com.cloud.network.dao; +import java.util.List; + import com.cloud.utils.db.GenericDao; public interface OpRouterMonitorServiceDao extends GenericDao { + int expungeByVmList(List vmIds, Long batchSize); + } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/OpRouterMonitorServiceDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/OpRouterMonitorServiceDaoImpl.java index 451320ac9b6c..a8e818cfb189 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/OpRouterMonitorServiceDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/OpRouterMonitorServiceDaoImpl.java @@ -17,10 +17,27 @@ package com.cloud.network.dao; -import com.cloud.utils.db.GenericDaoBase; +import java.util.List; + +import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + @Component public class OpRouterMonitorServiceDaoImpl extends GenericDaoBase implements OpRouterMonitorServiceDao { + @Override + public int expungeByVmList(List vmIds, Long batchSize) { + if (CollectionUtils.isEmpty(vmIds)) { + return 0; + } + SearchBuilder sb = createSearchBuilder(); + sb.and("vmIds", sb.entity().getId(), SearchCriteria.Op.IN); + SearchCriteria sc = sb.create(); + sc.setParameters("vmIds", vmIds.toArray()); + return batchExpunge(sc, batchSize); + } } 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 415b513b405a..9557c7465bff 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 @@ -35,6 +35,7 @@ import com.cloud.network.Network.Service; import com.cloud.network.PhysicalNetworkServiceProvider; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "physical_network_service_providers") @@ -109,6 +110,13 @@ public PhysicalNetworkServiceProviderVO(long physicalNetworkId, String name) { this.uuid = UUID.randomUUID().toString(); } + + @Override + public String toString() { + return String.format("PhysicalNetworkServiceProvider %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "providerName")); + } + @Override public long getId() { return id; 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 4811b59d31e5..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 @@ -125,7 +125,7 @@ public String getNetworkTag(long physicalNetworkId, TrafficType trafficType, Hyp sc = simulatorAllFieldsSearch.create(); } else if (hType == HypervisorType.Ovm) { sc = ovmAllFieldsSearch.create(); - } else if (hType == HypervisorType.BareMetal) { + } else if (hType == HypervisorType.BareMetal || hType == HypervisorType.External) { return null; } else if (hType == HypervisorType.Hyperv) { sc = hypervAllFieldsSearch.create(); @@ -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/PhysicalNetworkVO.java b/engine/schema/src/main/java/com/cloud/network/dao/PhysicalNetworkVO.java index 52ebe7596a41..68e023984a0c 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/PhysicalNetworkVO.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/PhysicalNetworkVO.java @@ -37,6 +37,7 @@ import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; /** * NetworkConfigurationVO contains information about a specific physical network. @@ -248,4 +249,11 @@ public void setUuid(String uuid) { public String getName() { return name; } + + @Override + public String toString() { + return String.format("PhysicalNetwork %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name")); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnDao.java b/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnDao.java index 8113c06c866e..f4120a138ac5 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnDao.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnDao.java @@ -33,4 +33,6 @@ public interface RemoteAccessVpnDao extends GenericDao List findByAccount(Long accountId); List listByNetworkId(Long networkId); + + List listByVpcId(Long vpcId); } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnDaoImpl.java index 484aa6f6631e..ccbc60a5562c 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnDaoImpl.java @@ -85,4 +85,11 @@ public List listByNetworkId(Long networkId) { sc.setParameters("networkId", networkId); return listBy(sc); } + + @Override + public List listByVpcId(Long vpcId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("vpcId", vpcId); + return listBy(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnVO.java b/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnVO.java index 95e3693a99c5..2439ea55b4a8 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnVO.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnVO.java @@ -18,6 +18,7 @@ import com.cloud.network.RemoteAccessVpn; import com.cloud.utils.db.Encrypt; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import javax.persistence.Column; import javax.persistence.Entity; @@ -86,6 +87,11 @@ public RemoteAccessVpnVO(long accountId, long domainId, Long networkId, long pub this.vpcId = vpcId; } + @Override + public String toString() { + return String.format("RemoteAccessVpn %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "uuid")); + } + @Override public State getState() { return state; 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/dao/Site2SiteCustomerGatewayVO.java b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteCustomerGatewayVO.java index 52741fdd9a54..e5394238c315 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteCustomerGatewayVO.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteCustomerGatewayVO.java @@ -29,6 +29,7 @@ import com.cloud.network.Site2SiteCustomerGateway; import com.cloud.utils.db.Encrypt; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @@ -110,6 +111,13 @@ public Site2SiteCustomerGatewayVO(String name, long accountId, long domainId, St this.ikeVersion = ikeVersion; } + @Override + public String toString() { + return String.format("Site2SiteCustomerGateway %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name")); + } + @Override public long getId() { return id; diff --git a/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnConnectionVO.java b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnConnectionVO.java index b032966dd5a2..4d6bee5c8614 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnConnectionVO.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnConnectionVO.java @@ -32,6 +32,7 @@ import com.cloud.network.Site2SiteVpnConnection; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @@ -182,4 +183,11 @@ public Class getEntityType() { public String getName() { return null; } + + @Override + public String toString() { + return String.format("Site2SiteVpnConnection %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "state")); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDao.java b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDao.java index d3fef252f506..3475003c2690 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDao.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDao.java @@ -20,4 +20,6 @@ public interface Site2SiteVpnGatewayDao extends GenericDao { Site2SiteVpnGatewayVO findByVpcId(long vpcId); + + Site2SiteVpnGatewayVO findByPublicIpAddress(long ipAddressId); } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDaoImpl.java index d1fde963217e..0aeefe90c29e 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDaoImpl.java @@ -35,6 +35,7 @@ public class Site2SiteVpnGatewayDaoImpl extends GenericDaoBase sc = AllFieldsSearch.create(); + sc.setParameters("ipAddressId", ipAddressId); + return findOneBy(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayVO.java b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayVO.java index 703c78c7b861..a5eb7efce234 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayVO.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayVO.java @@ -28,6 +28,7 @@ import com.cloud.network.Site2SiteVpnGateway; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @@ -70,6 +71,13 @@ public Site2SiteVpnGatewayVO(long accountId, long domainId, long addrId, long vp this.domainId = domainId; } + @Override + public String toString() { + return String.format("Site2SiteVpnGateway %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name")); + } + @Override public long getId() { return id; diff --git a/engine/schema/src/main/java/com/cloud/network/dao/SslCertDao.java b/engine/schema/src/main/java/com/cloud/network/dao/SslCertDao.java index 80bb44a1f4a0..1e73cd7b33ee 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/SslCertDao.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/SslCertDao.java @@ -22,4 +22,6 @@ public interface SslCertDao extends GenericDao { List listByAccountId(Long id); + + int removeByAccountId(long accountId); } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/SslCertDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/SslCertDaoImpl.java index 185c18aecd85..efadc009dfc2 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/SslCertDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/SslCertDaoImpl.java @@ -40,4 +40,10 @@ public List listByAccountId(Long accountId) { return listBy(sc); } + @Override + public int removeByAccountId(long accountId) { + SearchCriteria sc = listByAccountId.create(); + sc.setParameters("accountId", accountId); + return remove(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/element/NetrisProviderVO.java b/engine/schema/src/main/java/com/cloud/network/element/NetrisProviderVO.java new file mode 100644 index 000000000000..113678f7b010 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/network/element/NetrisProviderVO.java @@ -0,0 +1,265 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.element; + +import com.cloud.network.netris.NetrisProvider; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; +import java.util.UUID; + +@Entity +@Table(name = "netris_providers") +public class NetrisProviderVO implements NetrisProvider { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "name") + private String name; + + @Column(name = "zone_id") + private long zoneId; + + @Column(name = "host_id") + private long hostId; + + @Column(name = "url") + private String url; + + @Column(name = "username") + private String username; + + @Column(name = "password") + private String password; + + @Column(name = "site_name") + private String siteName; + + @Column(name = "tenant_name") + private String tenantName; + + @Column(name = "netris_tag") + private String netrisTag; + + @Column(name = "created") + private Date created; + + @Column(name = "removed") + private Date removed; + + public NetrisProviderVO() { + this.uuid = UUID.randomUUID().toString(); + } + + @Override + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + @Override + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + @Override + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public long getZoneId() { + return zoneId; + } + + public void setZoneId(long zoneId) { + this.zoneId = zoneId; + } + + public long getHostId() { + return hostId; + } + + public void setHostId(long hostId) { + this.hostId = hostId; + } + + @Override + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + @Override + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getSiteName() { + return siteName; + } + + public void setSiteName(String siteName) { + this.siteName = siteName; + } + + public String getTenantName() { + return tenantName; + } + + public void setTenantName(String tenantName) { + this.tenantName = tenantName; + } + + public String getNetrisTag() { + return netrisTag; + } + + public void setNetrisTag(String netrisTag) { + this.netrisTag = netrisTag; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public Date getRemoved() { + return removed; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } + + public static final class Builder { + private long zoneId; + private long hostId; + private String name; + private String url; + private String username; + private String password; + private String siteName; + private String tenantName; + private String netrisTag; + + public Builder() { + // Default constructor + } + + public Builder setZoneId(long zoneId) { + this.zoneId = zoneId; + return this; + } + + public Builder setHostId(long hostId) { + this.hostId = hostId; + return this; + } + + public Builder setName(String name) { + this.name = name; + return this; + } + + public Builder setUrl(String url) { + this.url = url; + return this; + } + + public Builder setUsername(String username) { + this.username = username; + return this; + } + + public Builder setPassword(String password) { + this.password = password; + return this; + } + + public Builder setSiteName(String siteName) { + this.siteName = siteName; + return this; + } + + public Builder setTenantName(String tenantName) { + this.tenantName = tenantName; + return this; + } + + public Builder setNetrisTag(String netrisTag) { + this.netrisTag = netrisTag; + return this; + } + + public NetrisProviderVO build() { + NetrisProviderVO provider = new NetrisProviderVO(); + provider.setZoneId(this.zoneId); + provider.setHostId(this.hostId); + provider.setUuid(UUID.randomUUID().toString()); + provider.setName(this.name); + provider.setUrl(this.url); + provider.setUsername(this.username); + provider.setPassword(this.password); + provider.setSiteName(this.siteName); + provider.setTenantName(this.tenantName); + provider.setNetrisTag(this.netrisTag); + provider.setCreated(new Date()); + return provider; + } + } +} diff --git a/engine/schema/src/main/java/com/cloud/network/element/NsxProviderVO.java b/engine/schema/src/main/java/com/cloud/network/element/NsxProviderVO.java new file mode 100644 index 000000000000..f08e08b1ca04 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/network/element/NsxProviderVO.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 com.cloud.network.element; + +import com.cloud.network.nsx.NsxProvider; +import com.cloud.utils.db.Encrypt; + +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 = "nsx_providers") +public class NsxProviderVO implements NsxProvider { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + long id; + + @Column(name = "zone_id") + private long zoneId; + + @Column(name = "host_id") + private long hostId; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "provider_name") + private String providerName; + + @Column(name = "hostname") + private String hostname; + + @Column(name = "port") + private String port = "443"; + + @Column(name = "username") + private String username; + + @Encrypt + @Column(name = "password") + private String password; + + @Column(name = "tier0_gateway") + private String tier0Gateway; + + @Column(name = "edge_cluster") + private String edgeCluster; + + @Column(name = "transport_zone") + private String transportZone; + + @Column(name = "created") + private Date created; + + @Column(name = "removed") + private Date removed; + public NsxProviderVO() { + this.uuid = UUID.randomUUID().toString(); + } + + @Override + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + @Override + public long getZoneId() { + return zoneId; + } + + public void setZoneId(long zoneId) { + this.zoneId = zoneId; + } + + public long getHostId() { + return hostId; + } + + public void setHostId(long hostId) { + this.hostId = hostId; + } + + @Override + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + @Override + public String getProviderName() { + return providerName; + } + + public void setProviderName(String providerName) { + this.providerName = providerName; + } + + @Override + public String getHostname() { + return hostname; + } + + public void setPort(String port) { + this.port = port; + } + + @Override + public String getPort() { + return port; + } + + public void setHostname(String hostname) { + this.hostname = hostname; + } + + @Override + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getTier0Gateway() { + return tier0Gateway; + } + + public void setTier0Gateway(String tier0Gateway) { + this.tier0Gateway = tier0Gateway; + } + + public String getEdgeCluster() { + return edgeCluster; + } + + public void setEdgeCluster(String edgeCluster) { + this.edgeCluster = edgeCluster; + } + + public String getTransportZone() { + return transportZone; + } + + public void setTransportZone(String transportZone) { + this.transportZone = transportZone; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public Date getRemoved() { + return removed; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } + + public static final class Builder { + private long zoneId; + private long hostId; + private String providerName; + private String hostname; + private String port; + private String username; + private String password; + private String tier0Gateway; + private String edgeCluster; + private String transportZone; + + + public Builder() { + // Default constructor + } + + public Builder setZoneId(long zoneId) { + this.zoneId = zoneId; + return this; + } + + public Builder setHostId(long hostId) { + this.hostId = hostId; + return this; + } + + public Builder setProviderName(String providerName) { + this.providerName = providerName; + return this; + } + + public Builder setHostname(String hostname) { + this.hostname = hostname; + return this; + } + + public Builder setPort(String port) { + this.port = port; + return this; + } + + public Builder setUsername(String username) { + this.username = username; + return this; + } + + public Builder setPassword(String password) { + this.password = password; + return this; + } + + public Builder setTier0Gateway(String tier0Gateway) { + this.tier0Gateway = tier0Gateway; + return this; + } + + public Builder setEdgeCluster(String edgeCluster) { + this.edgeCluster = edgeCluster; + return this; + } + + public Builder setTransportZone(String transportZone) { + this.transportZone = transportZone; + return this; + } + public NsxProviderVO build() { + NsxProviderVO provider = new NsxProviderVO(); + provider.setZoneId(this.zoneId); + provider.setHostId(this.hostId); + provider.setUuid(UUID.randomUUID().toString()); + provider.setProviderName(this.providerName); + provider.setHostname(this.hostname); + provider.setPort(this.port); + provider.setUsername(this.username); + provider.setPassword(this.password); + provider.setTier0Gateway(this.tier0Gateway); + provider.setEdgeCluster(this.edgeCluster); + provider.setTransportZone(this.transportZone); + provider.setCreated(new Date()); + return provider; + } + } +} 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 07b25e7a28ca..1dfdc5093a59 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 @@ -36,6 +36,7 @@ import com.cloud.utils.db.GenericDao; import com.cloud.utils.net.NetUtils; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "firewall_rules") @@ -258,7 +259,9 @@ public FirewallRuleVO(String xId, Long ipAddressId, Integer portStart, Integer p @Override public String toString() { - return new StringBuilder("Rule[").append(id).append("-").append(purpose).append("-").append(state).append("]").toString(); + return String.format("FirewallRule %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "networkId", "purpose", "state")); } @Override diff --git a/engine/schema/src/main/java/com/cloud/network/rules/PortForwardingRuleVO.java b/engine/schema/src/main/java/com/cloud/network/rules/PortForwardingRuleVO.java index e1a698881f33..576e2f8172e6 100644 --- a/engine/schema/src/main/java/com/cloud/network/rules/PortForwardingRuleVO.java +++ b/engine/schema/src/main/java/com/cloud/network/rules/PortForwardingRuleVO.java @@ -25,6 +25,7 @@ import javax.persistence.Enumerated; import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.Table; +import javax.persistence.Transient; import com.cloud.utils.net.Ip; @@ -47,21 +48,30 @@ public class PortForwardingRuleVO extends FirewallRuleVO implements PortForwardi @Column(name = "instance_id") private long virtualMachineId; + @Transient + List sourceCidrs; + public PortForwardingRuleVO() { } public PortForwardingRuleVO(String xId, long srcIpId, int srcPortStart, int srcPortEnd, Ip dstIp, int dstPortStart, int dstPortEnd, String protocol, long networkId, - long accountId, long domainId, long instanceId) { - super(xId, srcIpId, srcPortStart, srcPortEnd, protocol, networkId, accountId, domainId, Purpose.PortForwarding, null, null, null, null, null); + long accountId, long domainId, long instanceId, List sourceCidrs) { + super(xId, srcIpId, srcPortStart, srcPortEnd, protocol, networkId, accountId, domainId, Purpose.PortForwarding, sourceCidrs, null, null, null, null); this.destinationIpAddress = dstIp; this.virtualMachineId = instanceId; this.destinationPortStart = dstPortStart; this.destinationPortEnd = dstPortEnd; + this.sourceCidrs = sourceCidrs; } - public PortForwardingRuleVO(String xId, long srcIpId, int srcPort, Ip dstIp, int dstPort, String protocol, List sourceCidrs, long networkId, long accountId, - long domainId, long instanceId) { - this(xId, srcIpId, srcPort, srcPort, dstIp, dstPort, dstPort, protocol.toLowerCase(), networkId, accountId, domainId, instanceId); + public PortForwardingRuleVO(String xId, long srcIpId, int srcPortStart, int srcPortEnd, Ip dstIp, int dstPortStart, int dstPortEnd, String protocol, long networkId, + long accountId, long domainId, long instanceId) { + this(xId, srcIpId, srcPortStart, srcPortEnd, dstIp, dstPortStart, dstPortEnd, protocol.toLowerCase(), networkId, accountId, domainId, instanceId, null); + } + + public PortForwardingRuleVO(String xId, long srcIpId, int srcPort, Ip dstIp, int dstPort, String protocol, long networkId, long accountId, + long domainId, long instanceId) { + this(xId, srcIpId, srcPort, srcPort, dstIp, dstPort, dstPort, protocol.toLowerCase(), networkId, accountId, domainId, instanceId, null); } @Override @@ -106,4 +116,13 @@ public Long getRelated() { return null; } + public void setSourceCidrList(List sourceCidrs) { + this.sourceCidrs = sourceCidrs; + } + + @Override + public List getSourceCidrList() { + return sourceCidrs; + } + } diff --git a/engine/schema/src/main/java/com/cloud/network/rules/dao/PortForwardingRulesDao.java b/engine/schema/src/main/java/com/cloud/network/rules/dao/PortForwardingRulesDao.java index b89d04ad15a0..a737f1b9a205 100644 --- a/engine/schema/src/main/java/com/cloud/network/rules/dao/PortForwardingRulesDao.java +++ b/engine/schema/src/main/java/com/cloud/network/rules/dao/PortForwardingRulesDao.java @@ -47,4 +47,7 @@ public interface PortForwardingRulesDao extends GenericDao listByNetworkAndDestIpAddr(String ip4Address, long networkId); + + PortForwardingRuleVO findByNetworkAndPorts(long networkId, int startPort, int endPort); + int expungeByVmList(List vmIds, Long batchSize); } diff --git a/engine/schema/src/main/java/com/cloud/network/rules/dao/PortForwardingRulesDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/rules/dao/PortForwardingRulesDaoImpl.java index 29cba516d720..637f47731b47 100644 --- a/engine/schema/src/main/java/com/cloud/network/rules/dao/PortForwardingRulesDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/rules/dao/PortForwardingRulesDaoImpl.java @@ -20,6 +20,7 @@ import javax.inject.Inject; +import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; import com.cloud.network.dao.FirewallRulesCidrsDao; @@ -30,6 +31,9 @@ import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Op; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallback; +import com.cloud.utils.db.TransactionLegacy; @Component public class PortForwardingRulesDaoImpl extends GenericDaoBase implements PortForwardingRulesDao { @@ -41,7 +45,7 @@ public class PortForwardingRulesDaoImpl extends GenericDaoBase ActiveRulesSearchByAccount; @Inject - protected FirewallRulesCidrsDao _portForwardingRulesCidrsDao; + protected FirewallRulesCidrsDao portForwardingRulesCidrsDao; protected PortForwardingRulesDaoImpl() { super(); @@ -54,6 +58,8 @@ protected PortForwardingRulesDaoImpl() { AllFieldsSearch.and("vmId", AllFieldsSearch.entity().getVirtualMachineId(), Op.EQ); AllFieldsSearch.and("purpose", AllFieldsSearch.entity().getPurpose(), Op.EQ); AllFieldsSearch.and("dstIp", AllFieldsSearch.entity().getDestinationIpAddress(), Op.EQ); + AllFieldsSearch.and("sourcePortStart", AllFieldsSearch.entity().getSourcePortStart(), Op.EQ); + AllFieldsSearch.and("sourcePortEnd", AllFieldsSearch.entity().getSourcePortEnd(), Op.EQ); AllFieldsSearch.done(); ApplicationSearch = createSearchBuilder(); @@ -170,4 +176,64 @@ public PortForwardingRuleVO findByIdAndIp(long id, String secondaryIp) { sc.setParameters("dstIp", secondaryIp); return findOneBy(sc); } + + @Override + public PortForwardingRuleVO findByNetworkAndPorts(long networkId, int startPort, int endPort) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("networkId", networkId); + sc.setParameters("sourcePortStart", startPort); + sc.setParameters("sourcePortEnd", endPort); + return findOneBy(sc); + } + + @Override + public int expungeByVmList(List vmIds, Long batchSize) { + if (CollectionUtils.isEmpty(vmIds)) { + return 0; + } + SearchBuilder sb = createSearchBuilder(); + sb.and("vmIds", sb.entity().getVirtualMachineId(), SearchCriteria.Op.IN); + SearchCriteria sc = sb.create(); + sc.setParameters("vmIds", vmIds.toArray()); + return batchExpunge(sc, batchSize); + } + + public PortForwardingRuleVO persist(PortForwardingRuleVO portForwardingRule) { + return Transaction.execute((TransactionCallback) transactionStatus -> { + PortForwardingRuleVO dbPfRule = super.persist(portForwardingRule); + + portForwardingRulesCidrsDao.persist(portForwardingRule.getId(), portForwardingRule.getSourceCidrList()); + List cidrList = portForwardingRulesCidrsDao.getSourceCidrs(portForwardingRule.getId()); + portForwardingRule.setSourceCidrList(cidrList); + + return dbPfRule; + }); + + } + + @Override + public boolean update(Long id, PortForwardingRuleVO entity) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + txn.start(); + + boolean success = super.update(id, entity); + if (!success) { + return false; + } + + portForwardingRulesCidrsDao.updateSourceCidrsForRule(entity.getId(), entity.getSourceCidrList()); + txn.commit(); + + return true; + } + + @Override + public PortForwardingRuleVO findById(Long id) { + PortForwardingRuleVO rule = super.findById(id); + + List sourceCidrList = portForwardingRulesCidrsDao.getSourceCidrs(id); + rule.setSourceCidrList(sourceCidrList); + + return rule; + } } diff --git a/engine/schema/src/main/java/com/cloud/network/security/SecurityGroupRuleVO.java b/engine/schema/src/main/java/com/cloud/network/security/SecurityGroupRuleVO.java index 1980cd33d146..325a6efc867f 100644 --- a/engine/schema/src/main/java/com/cloud/network/security/SecurityGroupRuleVO.java +++ b/engine/schema/src/main/java/com/cloud/network/security/SecurityGroupRuleVO.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.network.security; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; + import java.util.UUID; import javax.persistence.Column; @@ -89,6 +91,13 @@ public SecurityGroupRuleVO(SecurityRuleType type, long securityGroupId, int from } } + @Override + public String toString() { + return String.format("SecurityGroupRule %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "type")); + } + @Override public long getId() { return id; diff --git a/engine/schema/src/main/java/com/cloud/network/security/SecurityGroupVMMapVO.java b/engine/schema/src/main/java/com/cloud/network/security/SecurityGroupVMMapVO.java index d12b9f9443fd..59699cba1d40 100644 --- a/engine/schema/src/main/java/com/cloud/network/security/SecurityGroupVMMapVO.java +++ b/engine/schema/src/main/java/com/cloud/network/security/SecurityGroupVMMapVO.java @@ -50,6 +50,9 @@ public class SecurityGroupVMMapVO implements InternalIdentity { @Column(name = "ip4_address", table = "nics", insertable = false, updatable = false) private String guestIpAddress; + @Column(name = "ip6_address", table = "nics", insertable = false, updatable = false) + private String guestIpv6Address; + @Column(name = "state", table = "vm_instance", insertable = false, updatable = false) private State vmState; @@ -77,6 +80,10 @@ public String getGuestIpAddress() { return guestIpAddress; } + public String getGuestIpv6Address() { + return guestIpv6Address; + } + public long getInstanceId() { return instanceId; } diff --git a/engine/schema/src/main/java/com/cloud/network/security/SecurityGroupVO.java b/engine/schema/src/main/java/com/cloud/network/security/SecurityGroupVO.java index ec1cfae43b63..940baaad18d7 100644 --- a/engine/schema/src/main/java/com/cloud/network/security/SecurityGroupVO.java +++ b/engine/schema/src/main/java/com/cloud/network/security/SecurityGroupVO.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.network.security; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; + import java.util.UUID; import javax.persistence.Column; @@ -60,6 +62,13 @@ public SecurityGroupVO(String name, String description, long domainId, long acco uuid = UUID.randomUUID().toString(); } + @Override + public String toString() { + return String.format("SecurityGroup %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name")); + } + @Override public long getId() { return id; diff --git a/engine/schema/src/main/java/com/cloud/network/security/dao/SecurityGroupVMMapDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/security/dao/SecurityGroupVMMapDaoImpl.java index a2ec95ccb679..803955a562c1 100644 --- a/engine/schema/src/main/java/com/cloud/network/security/dao/SecurityGroupVMMapDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/security/dao/SecurityGroupVMMapDaoImpl.java @@ -131,7 +131,7 @@ public List listBySecurityGroup(long securityGroupId, Stat SearchCriteria sc = ListBySecurityGroupAndStates.create(); sc.setParameters("securityGroupId", securityGroupId); sc.setParameters("states", (Object[])vmStates); - return listBy(sc, null, true); + return listBy(sc, null); } @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/NetworkACLItemVO.java b/engine/schema/src/main/java/com/cloud/network/vpc/NetworkACLItemVO.java index f28b3125a09d..4333d35d4733 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/NetworkACLItemVO.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/NetworkACLItemVO.java @@ -35,6 +35,7 @@ import com.cloud.utils.db.GenericDao; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "network_acl_item") @@ -168,7 +169,9 @@ public Date getCreated() { @Override public String toString() { - return new StringBuilder("Rule[").append(id).append("-").append("NetworkACL").append("-").append(state).append("]").toString(); + return String.format("NetworkACLItem %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "aclId", "state")); } @Override diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/NetworkACLVO.java b/engine/schema/src/main/java/com/cloud/network/vpc/NetworkACLVO.java index 280d5dfaf4b2..37b9e7ff296a 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/NetworkACLVO.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/NetworkACLVO.java @@ -89,7 +89,7 @@ public String getName() { @Override public String toString() { - return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "uuid", "name", "vpcId"); + return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "uuid", "name", "vpcId"); } public void setUuid(String uuid) { diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/StaticRouteVO.java b/engine/schema/src/main/java/com/cloud/network/vpc/StaticRouteVO.java index 2246bd6eed25..632d96819cd3 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/StaticRouteVO.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/StaticRouteVO.java @@ -27,6 +27,7 @@ import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; +import javax.persistence.Transient; import com.cloud.utils.db.GenericDao; @@ -42,7 +43,10 @@ public class StaticRouteVO implements StaticRoute { String uuid; @Column(name = "vpc_gateway_id", updatable = false) - long vpcGatewayId; + Long vpcGatewayId; + + @Column(name = "next_hop") + private String nextHop; @Column(name = "cidr") private String cidr; @@ -67,6 +71,9 @@ protected StaticRouteVO() { uuid = UUID.randomUUID().toString(); } + @Transient + boolean forVpn = false; + /** * @param vpcGatewayId * @param cidr @@ -74,7 +81,7 @@ protected StaticRouteVO() { * @param accountId TODO * @param domainId TODO */ - public StaticRouteVO(long vpcGatewayId, String cidr, Long vpcId, long accountId, long domainId) { + public StaticRouteVO(Long vpcGatewayId, String cidr, Long vpcId, long accountId, long domainId, String nextHop) { super(); this.vpcGatewayId = vpcGatewayId; this.cidr = cidr; @@ -82,14 +89,32 @@ public StaticRouteVO(long vpcGatewayId, String cidr, Long vpcId, long accountId, this.vpcId = vpcId; this.accountId = accountId; this.domainId = domainId; + this.nextHop = nextHop; + uuid = UUID.randomUUID().toString(); + } + + public StaticRouteVO(String cidr, Long vpcId, long accountId, long domainId, String nextHop, State state, boolean forVpn) { + super(); + this.cidr = cidr; + this.state = state; + this.vpcId = vpcId; + this.accountId = accountId; + this.domainId = domainId; + this.nextHop = nextHop; uuid = UUID.randomUUID().toString(); + this.forVpn = forVpn; } @Override - public long getVpcGatewayId() { + public Long getVpcGatewayId() { return vpcGatewayId; } + @Override + public String getNextHop() { + return nextHop; + } + @Override public String getCidr() { return cidr; @@ -145,4 +170,8 @@ public Class getEntityType() { public String getName() { return null; } + + public boolean isForVpn() { + return forVpn; + } } diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/VpcGatewayVO.java b/engine/schema/src/main/java/com/cloud/network/vpc/VpcGatewayVO.java index 72f6a89e70fc..b1d4df35d4ca 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/VpcGatewayVO.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/VpcGatewayVO.java @@ -29,6 +29,7 @@ import javax.persistence.Table; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "vpc_gateways") @@ -163,9 +164,9 @@ public long getNetworkId() { @Override public String toString() { - StringBuilder buf = new StringBuilder("VpcGateway["); - buf.append(id).append("|").append(ip4Address.toString()).append("|").append(vpcId).append("]"); - return buf.toString(); + return String.format("VpcGateway %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "vpcId", "ip4Address")); } @Override 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 aa26f16568a9..9320a37bc96e 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 @@ -28,7 +28,9 @@ import javax.persistence.Id; import javax.persistence.Table; +import com.cloud.offering.NetworkOffering; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "vpc_offerings") @@ -58,6 +60,9 @@ public class VpcOfferingVO implements VpcOffering { @Column(name = "default") boolean isDefault = false; + @Column(name = "network_mode") + NetworkOffering.NetworkMode networkMode; + @Column(name = GenericDao.REMOVED_COLUMN) Date removed; @@ -79,6 +84,13 @@ public class VpcOfferingVO implements VpcOffering { @Column(name = "sort_key") int sortKey; + @Column(name="routing_mode") + @Enumerated(value = EnumType.STRING) + private NetworkOffering.RoutingMode routingMode; + + @Column(name = "specify_as_number") + private Boolean specifyAsNumber = false; + public VpcOfferingVO() { this.uuid = UUID.randomUUID().toString(); } @@ -144,14 +156,23 @@ public boolean isDefault() { return isDefault; } + public NetworkOffering.NetworkMode getNetworkMode() { + return networkMode; + } + + public void setNetworkMode(NetworkOffering.NetworkMode networkMode) { + this.networkMode = networkMode; + } + public void setUniqueName(String uniqueName) { this.uniqueName = uniqueName; } @Override public String toString() { - StringBuilder buf = new StringBuilder("[VPC Offering ["); - return buf.append(id).append("-").append(name).append("]").toString(); + return String.format("VPCOffering %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name")); } public void setName(String name) { @@ -204,4 +225,21 @@ public int getSortKey() { return sortKey; } + @Override + public NetworkOffering.RoutingMode getRoutingMode() { + return routingMode; + } + + public void setRoutingMode(NetworkOffering.RoutingMode routingMode) { + this.routingMode = routingMode; + } + + @Override + public Boolean isSpecifyAsNumber() { + return specifyAsNumber; + } + + public void setSpecifyAsNumber(Boolean specifyAsNumber) { + this.specifyAsNumber = specifyAsNumber; + } } 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 c2024e06c51b..e942eadb8ffb 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 @@ -28,6 +28,7 @@ import javax.persistence.Transient; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "vpc") @@ -104,6 +105,9 @@ public class VpcVO implements Vpc { @Column(name = "ip6Dns2") String ip6Dns2; + @Column(name = "use_router_ip_resolver") + boolean useRouterIpResolver = false; + @Transient boolean rollingRestart = false; @@ -159,6 +163,10 @@ public String getCidr() { return cidr; } + public void setCidr(String cidr) { + this.cidr = cidr; + } + @Override public long getDomainId() { return domainId; @@ -206,8 +214,9 @@ public void setDisplayText(final String displayText) { @Override public String toString() { - final StringBuilder buf = new StringBuilder("[VPC ["); - return buf.append(id).append("-").append(name).append("]").toString(); + return String.format("VPC %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name")); } @Override @@ -303,4 +312,13 @@ public String getIp6Dns1() { public String getIp6Dns2() { return ip6Dns2; } + + @Override + public boolean useRouterIpAsResolver() { + return useRouterIpResolver; + } + + public void setUseRouterIpResolver(boolean useRouterIpResolver) { + this.useRouterIpResolver = useRouterIpResolver; + } } diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingDao.java b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingDao.java index 264a1ebc75e8..aa17723f0b17 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingDao.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingDao.java @@ -33,4 +33,6 @@ public interface VpcOfferingDao extends GenericDao { NetUtils.InternetProtocol getVpcOfferingInternetProtocol(long offeringId); boolean isIpv6Supported(long offeringId); + + boolean isRoutedVpc(long offeringId); } diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingDaoImpl.java index 1cc6a21da76c..b83fd8913059 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingDaoImpl.java @@ -19,6 +19,7 @@ import javax.inject.Inject; +import com.cloud.offering.NetworkOffering; import org.apache.cloudstack.api.ApiConstants; import org.springframework.stereotype.Component; @@ -84,4 +85,9 @@ public boolean isIpv6Supported(long offeringId) { NetUtils.InternetProtocol internetProtocol = getVpcOfferingInternetProtocol(offeringId); return NetUtils.InternetProtocol.isIpv6EnabledProtocol(internetProtocol); } + + @Override + public boolean isRoutedVpc(long offeringId) { + return NetworkOffering.NetworkMode.ROUTED.equals(findById(offeringId).getNetworkMode()); + } } 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 06cfd25e670b..020536e97ec9 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDao.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDao.java @@ -18,6 +18,7 @@ import java.util.List; +import com.cloud.network.Network; import com.cloud.network.Network.Service; import com.cloud.network.vpc.VpcOfferingServiceMapVO; import com.cloud.utils.db.GenericDao; @@ -37,4 +38,8 @@ public interface VpcOfferingServiceMapDao extends GenericDao listProvidersForServiceForVpcOffering(long vpcOfferingId, Service service); + } diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java index c7400f6edfd5..dcb1becf9e88 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java @@ -19,6 +19,7 @@ import java.util.List; +import com.cloud.network.Network; import org.springframework.stereotype.Component; import com.cloud.network.Network.Service; @@ -110,4 +111,22 @@ public VpcOfferingServiceMapVO findByServiceProviderAndOfferingId(String service return findOneBy(sc); } + + @Override + public boolean isProviderForVpcOffering(Network.Provider provider, long vpcOfferingId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("vpcOffId", vpcOfferingId); + sc.setParameters("provider", provider.getName()); + return findOneBy(sc) != null; + } + + @Override + public List listProvidersForServiceForVpcOffering(long vpcOfferingId, Service service) { + SearchCriteria sc = AllFieldsSearch.create(); + + sc.setParameters("vpcOffId", vpcOfferingId); + sc.setParameters("service", service.getName()); + + return customSearch(sc, null); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcServiceMapDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcServiceMapDaoImpl.java index 753c45fcc789..a5c4c83ff0fe 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcServiceMapDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcServiceMapDaoImpl.java @@ -68,8 +68,15 @@ public boolean areServicesSupportedInVpc(long vpcId, Service... services) { @Override public boolean canProviderSupportServiceInVpc(long vpcId, Service service, Provider provider) { - // TODO Auto-generated method stub - return false; + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("vpcId", vpcId); + sc.setParameters("service", service.getName()); + sc.setParameters("provider", provider.getName()); + if (findOneBy(sc) != null) { + return true; + } else { + return false; + } } @Override diff --git a/engine/schema/src/main/java/com/cloud/offerings/NetworkOfferingVO.java b/engine/schema/src/main/java/com/cloud/offerings/NetworkOfferingVO.java index ae5e6fb95ea9..904c8e646eb5 100644 --- a/engine/schema/src/main/java/com/cloud/offerings/NetworkOfferingVO.java +++ b/engine/schema/src/main/java/com/cloud/offerings/NetworkOfferingVO.java @@ -32,6 +32,7 @@ import com.cloud.network.Networks.TrafficType; import com.cloud.offering.NetworkOffering; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "network_offerings") @@ -133,8 +134,8 @@ public class NetworkOfferingVO implements NetworkOffering { @Column(name = "for_vpc") boolean forVpc; - @Column(name = "for_tungsten") - boolean forTungsten = false; + @Column(name = "network_mode") + NetworkMode networkMode; @Column(name = "egress_default_policy") boolean egressdefaultpolicy; @@ -168,6 +169,13 @@ public String getDisplayText() { @Column(name="service_package_id") String servicePackageUuid = null; + @Column(name="routing_mode") + @Enumerated(value = EnumType.STRING) + private RoutingMode routingMode; + + @Column(name = "specify_as_number") + private Boolean specifyAsNumber = false; + @Override public boolean isKeepAliveEnabled() { return keepAliveEnabled; @@ -187,12 +195,12 @@ public void setForVpc(boolean isForVpc) { } @Override - public boolean isForTungsten() { - return forTungsten; + public NetworkMode getNetworkMode() { + return networkMode; } - public void setForTungsten(boolean forTungsten) { - this.forTungsten = forTungsten; + public void setNetworkMode(NetworkMode networkMode) { + this.networkMode = networkMode; } @Override @@ -440,8 +448,8 @@ public NetworkOfferingVO(String name, Network.GuestType guestType, boolean speci @Override public String toString() { - StringBuilder buf = new StringBuilder("[Network Offering ["); - return buf.append(id).append("-").append(trafficType).append("-").append(name).append("]").toString(); + return String.format("NetworkOffering %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "trafficType")); } @Override @@ -558,4 +566,21 @@ public void setSupportsVmAutoScaling(boolean supportsVmAutoScaling) { public boolean isSupportsVmAutoScaling() { return supportsVmAutoScaling; } + + @Override + public RoutingMode getRoutingMode() { + return routingMode; + } + + public void setRoutingMode(RoutingMode routingMode) { + this.routingMode = routingMode; + } + + public Boolean isSpecifyAsNumber() { + return specifyAsNumber; + } + + public void setSpecifyAsNumber(Boolean specifyAsNumber) { + this.specifyAsNumber = specifyAsNumber; + } } diff --git a/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingDao.java b/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingDao.java index 381d2144df16..abb63a10d065 100644 --- a/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingDao.java +++ b/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingDao.java @@ -76,4 +76,6 @@ public interface NetworkOfferingDao extends GenericDao NetUtils.InternetProtocol getNetworkOfferingInternetProtocol(long offeringId, NetUtils.InternetProtocol defaultProtocol); boolean isIpv6Supported(long offeringId); + + boolean isRoutedNetwork(long offeringId); } diff --git a/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingDaoImpl.java b/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingDaoImpl.java index 823ea36b97f5..9bc74b139320 100644 --- a/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/offerings/dao/NetworkOfferingDaoImpl.java @@ -292,4 +292,9 @@ public boolean isIpv6Supported(long offeringId) { NetUtils.InternetProtocol internetProtocol = getNetworkOfferingInternetProtocol(offeringId); return NetUtils.InternetProtocol.isIpv6EnabledProtocol(internetProtocol); } + + @Override + public boolean isRoutedNetwork(long offeringId) { + return NetworkOffering.NetworkMode.ROUTED.equals(findById(offeringId).getNetworkMode()); + } } diff --git a/engine/schema/src/main/java/com/cloud/projects/ProjectInvitationVO.java b/engine/schema/src/main/java/com/cloud/projects/ProjectInvitationVO.java index 36e772edd3a8..887939311b24 100644 --- a/engine/schema/src/main/java/com/cloud/projects/ProjectInvitationVO.java +++ b/engine/schema/src/main/java/com/cloud/projects/ProjectInvitationVO.java @@ -29,6 +29,7 @@ import javax.persistence.Table; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "project_invitations") @@ -127,9 +128,9 @@ public void setState(State state) { @Override public String toString() { - StringBuilder buf = new StringBuilder("ProjectInvitation["); - buf.append(id).append("|projectId=").append(projectId).append("|accountId=").append(forAccountId).append("]"); - return buf.toString(); + return String.format("ProjectInvitation %s.", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "projectId", "forAccountId")); } @Override diff --git a/engine/schema/src/main/java/com/cloud/projects/ProjectVO.java b/engine/schema/src/main/java/com/cloud/projects/ProjectVO.java index c8faa00812c5..4ac34eeab4c2 100644 --- a/engine/schema/src/main/java/com/cloud/projects/ProjectVO.java +++ b/engine/schema/src/main/java/com/cloud/projects/ProjectVO.java @@ -117,7 +117,9 @@ public Date getRemoved() { @Override public String toString() { - return String.format("Project %s.", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "name", "uuid", "domainId")); + return String.format("Project %s.", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "domainId")); } @Override diff --git a/engine/schema/src/main/java/com/cloud/projects/dao/ProjectAccountDao.java b/engine/schema/src/main/java/com/cloud/projects/dao/ProjectAccountDao.java index 730182a1cb56..f4b2f6460020 100644 --- a/engine/schema/src/main/java/com/cloud/projects/dao/ProjectAccountDao.java +++ b/engine/schema/src/main/java/com/cloud/projects/dao/ProjectAccountDao.java @@ -47,6 +47,8 @@ public interface ProjectAccountDao extends GenericDao { void removeAccountFromProjects(long accountId); + void removeUserFromProjects(long userId); + boolean canUserModifyProject(long projectId, long accountId, long userId); List listUsersOrAccountsByRole(long id); diff --git a/engine/schema/src/main/java/com/cloud/projects/dao/ProjectAccountDaoImpl.java b/engine/schema/src/main/java/com/cloud/projects/dao/ProjectAccountDaoImpl.java index 8947cc600b38..b6eb6d44cea8 100644 --- a/engine/schema/src/main/java/com/cloud/projects/dao/ProjectAccountDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/projects/dao/ProjectAccountDaoImpl.java @@ -192,6 +192,17 @@ public void removeAccountFromProjects(long accountId) { } } + @Override + public void removeUserFromProjects(long userId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("userId", userId); + + int removedCount = remove(sc); + if (removedCount > 0) { + logger.debug(String.format("Removed user [%s] from %s project(s).", userId, removedCount)); + } + } + @Override public boolean canUserModifyProject(long projectId, long accountId, long userId) { SearchCriteria sc = AllFieldsSearch.create(); diff --git a/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDao.java b/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDao.java index 3724e03d9d06..cee5cc15e112 100644 --- a/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDao.java +++ b/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDao.java @@ -22,10 +22,13 @@ import com.cloud.utils.db.GenericDao; import org.apache.cloudstack.api.response.ResourceIconResponse; +import java.util.Collection; import java.util.List; public interface ResourceIconDao extends GenericDao { ResourceIconResponse newResourceIconResponse(ResourceIcon resourceIconVO); ResourceIconVO findByResourceUuid(String resourceUuid, ResourceTag.ResourceObjectType resourceType); + List listByResourceTypeAndIds(ResourceTag.ResourceObjectType resourceType, Collection resourceIds); + List listByResourceTypeAndUuids(ResourceTag.ResourceObjectType resourceType, Collection resourceUuids); List listResourceIcons(List resourceUuids, ResourceTag.ResourceObjectType resourceType); } diff --git a/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDaoImpl.java b/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDaoImpl.java index 1ae01bfc1ec2..49a1fe7a560a 100644 --- a/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDaoImpl.java @@ -24,8 +24,10 @@ import com.cloud.utils.db.SearchCriteria; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.response.ResourceIconResponse; +import org.apache.commons.collections.CollectionUtils; import java.util.ArrayList; +import java.util.Collection; import java.util.List; public class ResourceIconDaoImpl extends GenericDaoBase implements ResourceIconDao { @@ -58,11 +60,36 @@ public ResourceIconVO findByResourceUuid(String resourceUuid, ResourceTag.Resour } @Override - public List listResourceIcons(List resourceUuids, ResourceTag.ResourceObjectType resourceType) { + public List listByResourceTypeAndIds(ResourceTag.ResourceObjectType resourceType, + Collection resourceIds) { + if (CollectionUtils.isEmpty(resourceIds)) { + return new ArrayList<>(); + } + SearchBuilder sb = createSearchBuilder(); + sb.and("resourceId", sb.entity().getResourceId(), SearchCriteria.Op.IN); + sb.and("resourceType", sb.entity().getResourceType(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("resourceId", resourceIds.toArray()); + sc.setParameters("resourceType", resourceType); + return listBy(sc); + } + + @Override + public List listByResourceTypeAndUuids(ResourceTag.ResourceObjectType resourceType, + Collection resourceUuids) { + if (CollectionUtils.isEmpty(resourceUuids)) { + return new ArrayList<>(); + } SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("uuid", resourceUuids.toArray()); sc.setParameters("resourceType", resourceType); - List resourceIcons = listBy(sc); + return listBy(sc); + } + + @Override + public List listResourceIcons(List resourceUuids, ResourceTag.ResourceObjectType resourceType) { + List resourceIcons = listByResourceTypeAndUuids(resourceType, resourceUuids); List iconResponses = new ArrayList<>(); for (ResourceIconVO resourceIcon : resourceIcons) { ResourceIconResponse response = new ResourceIconResponse(); diff --git a/engine/schema/src/main/java/com/cloud/secstorage/CommandExecLogDao.java b/engine/schema/src/main/java/com/cloud/secstorage/CommandExecLogDao.java index 98fc8c8687b8..5023aaa3794c 100644 --- a/engine/schema/src/main/java/com/cloud/secstorage/CommandExecLogDao.java +++ b/engine/schema/src/main/java/com/cloud/secstorage/CommandExecLogDao.java @@ -17,10 +17,12 @@ package com.cloud.secstorage; import java.util.Date; +import java.util.List; import com.cloud.utils.db.GenericDao; public interface CommandExecLogDao extends GenericDao { public void expungeExpiredRecords(Date cutTime); public Integer getCopyCmdCountForSSVM(Long id); + int expungeByVmList(List vmIds, Long batchSize); } diff --git a/engine/schema/src/main/java/com/cloud/secstorage/CommandExecLogDaoImpl.java b/engine/schema/src/main/java/com/cloud/secstorage/CommandExecLogDaoImpl.java index f89a1bbf4ccb..8229c3a62fc2 100644 --- a/engine/schema/src/main/java/com/cloud/secstorage/CommandExecLogDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/secstorage/CommandExecLogDaoImpl.java @@ -19,6 +19,7 @@ import java.util.Date; import java.util.List; +import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; import com.cloud.utils.db.GenericDaoBase; @@ -54,7 +55,18 @@ public Integer getCopyCmdCountForSSVM(Long id) { SearchCriteria sc = CommandSearch.create(); sc.setParameters("host_id", id); sc.setParameters("command_name", "CopyCommand"); - List copyCmds = customSearch(sc, null); - return copyCmds.size(); + return getCount(sc); + } + + @Override + public int expungeByVmList(List vmIds, Long batchSize) { + if (CollectionUtils.isEmpty(vmIds)) { + return 0; + } + SearchBuilder sb = createSearchBuilder(); + sb.and("vmIds", sb.entity().getInstanceId(), SearchCriteria.Op.IN); + SearchCriteria sc = sb.create(); + sc.setParameters("vmIds", vmIds.toArray()); + return batchExpunge(sc, batchSize); } } diff --git a/engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java b/engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java index 31e4b073c139..cfe8049f5b2c 100644 --- a/engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java +++ b/engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java @@ -124,6 +124,15 @@ public class ServiceOfferingVO implements ServiceOffering { @Column(name = "dynamic_scaling_enabled") private boolean dynamicScalingEnabled = true; + @Column(name = "vgpu_profile_id") + private Long vgpuProfileId; + + @Column(name = "gpu_count") + private Integer gpuCount; + + @Column(name = "gpu_display") + private Boolean gpuDisplay; + // This is a delayed load value. If the value is null, // then this field has not been loaded yet. // Call service offering dao to load it. @@ -194,10 +203,12 @@ public ServiceOfferingVO(ServiceOfferingVO offering) { limitCpuUse = offering.getLimitCpuUse(); volatileVm = offering.isVolatileVm(); hostTag = offering.getHostTag(); - vmType = offering.getSystemVmType(); + vmType = offering.getVmType(); systemUse = offering.isSystemUse(); dynamicScalingEnabled = offering.isDynamicScalingEnabled(); diskOfferingStrictness = offering.diskOfferingStrictness; + vgpuProfileId = offering.vgpuProfileId; + gpuCount = offering.gpuCount; } @Override @@ -278,7 +289,7 @@ public String getHostTag() { } @Override - public String getSystemVmType() { + public String getVmType() { return vmType; } @@ -445,4 +456,30 @@ public Boolean getDiskOfferingStrictness() { public void setDiskOfferingStrictness(boolean diskOfferingStrictness) { this.diskOfferingStrictness = diskOfferingStrictness; } + + @Override + public Long getVgpuProfileId() { + return vgpuProfileId; + } + + public void setVgpuProfileId(Long vgpuProfileId) { + this.vgpuProfileId = vgpuProfileId; + } + + @Override + public Integer getGpuCount() { + return gpuCount; + } + + public void setGpuCount(Integer gpuCount) { + this.gpuCount = gpuCount; + } + + public Boolean getGpuDisplay() { + return gpuDisplay; + } + + public void setGpuDisplay(Boolean gpuDisplay) { + this.gpuDisplay = gpuDisplay; + } } diff --git a/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDao.java b/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDao.java index e2fc5b49ae84..d3bab4fcbe27 100644 --- a/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDao.java +++ b/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDao.java @@ -22,6 +22,7 @@ import com.cloud.service.ServiceOfferingVO; import com.cloud.storage.Storage.ProvisioningType; import com.cloud.utils.db.GenericDao; +import com.cloud.utils.db.SearchBuilder; import com.cloud.vm.VirtualMachine; /* @@ -54,5 +55,9 @@ List createSystemServiceOfferings(String name, String uniqueN List listPublicByCpuAndMemory(Integer cpus, Integer memory); - ServiceOfferingVO findServiceOfferingByComputeOnlyDiskOffering(long diskOfferingId); + ServiceOfferingVO findServiceOfferingByComputeOnlyDiskOffering(long diskOfferingId, boolean includingRemoved); + + List listIdsByHostTag(String tag); + + void addCheckForGpuEnabled(SearchBuilder serviceOfferingSearch, Boolean gpuEnabled); } diff --git a/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDaoImpl.java b/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDaoImpl.java index f83ab7f98a46..f360770ad686 100644 --- a/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDaoImpl.java @@ -34,11 +34,12 @@ import com.cloud.storage.Storage.ProvisioningType; import com.cloud.utils.db.DB; 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.exception.CloudRuntimeException; import com.cloud.vm.VirtualMachine; -import com.cloud.vm.dao.UserVmDetailsDao; +import com.cloud.vm.dao.VMInstanceDetailsDao; @Component @DB() @@ -47,7 +48,7 @@ public class ServiceOfferingDaoImpl extends GenericDaoBase dynamicOffering = userVmDetailsDao.listDetailsKeyPairs(vmId); + Map dynamicOffering = vmInstanceDetailsDao.listDetailsKeyPairs(vmId); return getComputeOffering(offering, dynamicOffering); } return offering; @@ -189,7 +190,7 @@ public ServiceOfferingVO findByIdIncludingRemoved(Long vmId, long serviceOfferin if (vmId == null) { throw new CloudRuntimeException("missing argument vmId"); } - Map dynamicOffering = userVmDetailsDao.listDetailsKeyPairs(vmId); + Map dynamicOffering = vmInstanceDetailsDao.listDetailsKeyPairs(vmId); return getComputeOffering(offering, dynamicOffering); } return offering; @@ -282,13 +283,42 @@ public List listPublicByCpuAndMemory(Integer cpus, Integer me } @Override - public ServiceOfferingVO findServiceOfferingByComputeOnlyDiskOffering(long diskOfferingId) { + public ServiceOfferingVO findServiceOfferingByComputeOnlyDiskOffering(long diskOfferingId, boolean includingRemoved) { SearchCriteria sc = SearchComputeOfferingByComputeOnlyDiskOffering.create(); sc.setParameters("disk_offering_id", diskOfferingId); - List vos = listBy(sc); + List vos = includingRemoved ? listIncludingRemovedBy(sc) : listBy(sc); if (vos.size() == 0) { return null; } return vos.get(0); } + + @Override + public List listIdsByHostTag(String tag) { + GenericSearchBuilder sb = createSearchBuilder(Long.class); + sb.selectFields(sb.entity().getId()); + sb.and("tagNotNull", sb.entity().getHostTag(), SearchCriteria.Op.NNULL); + sb.and().op("tagEq", sb.entity().getHostTag(), SearchCriteria.Op.EQ); + sb.or("tagStartLike", sb.entity().getHostTag(), SearchCriteria.Op.LIKE); + sb.or("tagMidLike", sb.entity().getHostTag(), SearchCriteria.Op.LIKE); + sb.or("tagEndLike", sb.entity().getHostTag(), SearchCriteria.Op.LIKE); + sb.cp(); + sb.done(); + SearchCriteria sc = sb.create(); + + sc.setParameters("tagEq", tag); + sc.setParameters("tagStartLike", tag + ",%"); + sc.setParameters("tagMidLike", "%," + tag + ",%"); + sc.setParameters("tagEndLike", "%," + tag); + return customSearch(sc, null); + } + + @Override + public void addCheckForGpuEnabled(SearchBuilder serviceOfferingSearch, Boolean gpuEnabled) { + if (gpuEnabled) { + serviceOfferingSearch.and("gpuEnabled", serviceOfferingSearch.entity().getVgpuProfileId(), SearchCriteria.Op.NNULL); + } else { + serviceOfferingSearch.and("gpuDisabled", serviceOfferingSearch.entity().getVgpuProfileId(), SearchCriteria.Op.NULL); + } + } } diff --git a/engine/schema/src/main/java/com/cloud/storage/BucketVO.java b/engine/schema/src/main/java/com/cloud/storage/BucketVO.java index 181b02e5a1b0..a54c1dd9b081 100644 --- a/engine/schema/src/main/java/com/cloud/storage/BucketVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/BucketVO.java @@ -19,8 +19,7 @@ import com.cloud.utils.db.GenericDao; import com.google.gson.annotations.Expose; import org.apache.cloudstack.storage.object.Bucket; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import javax.persistence.Column; import javax.persistence.Entity; @@ -97,17 +96,23 @@ public class BucketVO implements Bucket { String uuid; public BucketVO() { + this.uuid = UUID.randomUUID().toString(); + } + + public BucketVO(String name) { + this.uuid = UUID.randomUUID().toString(); + this.name = name; + this.state = State.Allocated; } public BucketVO(long accountId, long domainId, long objectStoreId, String name, Integer quota, boolean versioning, - boolean encryption, boolean objectLock, String policy) - { + boolean encryption, boolean objectLock, String policy) { this.accountId = accountId; this.domainId = domainId; this.objectStoreId = objectStoreId; this.name = name; - state = State.Allocated; - uuid = UUID.randomUUID().toString(); + this.state = State.Allocated; + this.uuid = UUID.randomUUID().toString(); this.quota = quota; this.versioning = versioning; this.encryption = encryption; @@ -251,7 +256,8 @@ public Class getEntityType() { @Override public String toString() { - return String.format("Bucket %s", new ToStringBuilder(this, ToStringStyle.JSON_STYLE).append("uuid", getUuid()).append("name", getName()) - .append("ObjectStoreId", getObjectStoreId()).toString()); + return String.format("Bucket %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "objectStoreId")); } } 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 b4f112f98e8b..7f6b6d8adf0e 100644 --- a/engine/schema/src/main/java/com/cloud/storage/DiskOfferingVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/DiskOfferingVO.java @@ -34,6 +34,7 @@ import com.cloud.offering.DiskOffering; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "disk_offering") @@ -576,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; } @@ -588,4 +589,11 @@ public boolean getDiskSizeStrictness() { public void setDiskSizeStrictness(boolean diskSizeStrictness) { this.diskSizeStrictness = diskSizeStrictness; } + + @Override + public String toString() { + return String.format("DiskOffering %s.", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name")); + } } diff --git a/engine/schema/src/main/java/com/cloud/storage/GuestOSCategoryVO.java b/engine/schema/src/main/java/com/cloud/storage/GuestOSCategoryVO.java index 36773e351e36..642705ffcbe4 100644 --- a/engine/schema/src/main/java/com/cloud/storage/GuestOSCategoryVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/GuestOSCategoryVO.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.storage; +import java.util.Date; import java.util.UUID; import javax.persistence.Column; @@ -25,6 +26,8 @@ import javax.persistence.Id; import javax.persistence.Table; +import com.cloud.utils.db.GenericDao; + @Entity @Table(name = "guest_os_category") public class GuestOSCategoryVO implements GuestOsCategory { @@ -39,6 +42,26 @@ public class GuestOSCategoryVO implements GuestOsCategory { @Column(name = "uuid") String uuid = UUID.randomUUID().toString(); + @Column(name = "featured") + boolean featured; + + @Column(name = "sort_key") + private int sortKey; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + @Column(name = GenericDao.REMOVED_COLUMN) + private Date removed; + + public GuestOSCategoryVO() { + } + + public GuestOSCategoryVO(String name, boolean featured) { + this.name = name; + this.featured = featured; + } + @Override public long getId() { return id; @@ -59,7 +82,25 @@ public String getUuid() { return this.uuid; } - public void setUuid(String uuid) { - this.uuid = uuid; + @Override + public boolean isFeatured() { + return featured; + } + + public void setFeatured(Boolean featured) { + this.featured = featured; + } + + public void setSortKey(int key) { + sortKey = key; + } + + public int getSortKey() { + return sortKey; + } + + @Override + public Date getCreated() { + return created; } } diff --git a/engine/schema/src/main/java/com/cloud/storage/GuestOSHypervisorVO.java b/engine/schema/src/main/java/com/cloud/storage/GuestOSHypervisorVO.java index e900d28a8641..cae1e1b7eeed 100644 --- a/engine/schema/src/main/java/com/cloud/storage/GuestOSHypervisorVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/GuestOSHypervisorVO.java @@ -20,6 +20,7 @@ import java.util.UUID; import javax.persistence.Column; +import javax.persistence.Convert; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; @@ -28,6 +29,7 @@ import com.cloud.hypervisor.Hypervisor; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.util.HypervisorTypeConverter; @Entity @Table(name = "guest_os_hypervisor") @@ -44,6 +46,7 @@ public class GuestOSHypervisorVO implements GuestOSHypervisor { String guestOsName; @Column(name = "hypervisor_type") + @Convert(converter = HypervisorTypeConverter.class) String hypervisorType; @Column(name = "hypervisor_version") 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 c78485868265..299c6380ab63 100644 --- a/engine/schema/src/main/java/com/cloud/storage/SnapshotPolicyVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/SnapshotPolicyVO.java @@ -27,6 +27,7 @@ import com.cloud.storage.snapshot.SnapshotPolicy; import com.cloud.utils.DateUtil.IntervalType; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "snapshot_policy") @@ -58,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; @@ -65,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; @@ -74,6 +81,15 @@ 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 + public String toString() { + return String.format("SnapshotPolicy %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "volumeId")); } @Override @@ -152,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/SnapshotScheduleVO.java b/engine/schema/src/main/java/com/cloud/storage/SnapshotScheduleVO.java index 80a890aacad6..5e013e76d3c8 100644 --- a/engine/schema/src/main/java/com/cloud/storage/SnapshotScheduleVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/SnapshotScheduleVO.java @@ -29,6 +29,7 @@ import javax.persistence.TemporalType; import com.cloud.storage.snapshot.SnapshotSchedule; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "snapshot_schedule") @@ -71,6 +72,13 @@ public SnapshotScheduleVO(long volumeId, long policyId, Date scheduledTimestamp) this.asyncJobId = null; } + @Override + public String toString() { + return String.format("SnapshotSchedule %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "volumeId", "policyId")); + } + @Override public long getId() { return id; 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 e9d6df85c2f2..4a504333344f 100644 --- a/engine/schema/src/main/java/com/cloud/storage/SnapshotVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/SnapshotVO.java @@ -20,6 +20,7 @@ import java.util.UUID; import javax.persistence.Column; +import javax.persistence.Convert; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; @@ -28,12 +29,12 @@ import javax.persistence.Id; import javax.persistence.Table; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; +import org.apache.cloudstack.util.HypervisorTypeConverter; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.utils.db.GenericDao; import com.google.gson.annotations.Expose; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "snapshots") @@ -89,7 +90,7 @@ public class SnapshotVO implements Snapshot { Date removed; @Column(name = "hypervisor_type") - @Enumerated(value = EnumType.STRING) + @Convert(converter = HypervisorTypeConverter.class) HypervisorType hypervisorType; @Expose @@ -281,7 +282,8 @@ public Class getEntityType() { @Override public String toString() { - return String.format("Snapshot %s", new ToStringBuilder(this, ToStringStyle.JSON_STYLE).append("uuid", getUuid()).append("name", getName()) - .append("volumeId", getVolumeId()).toString()); + return String.format("Snapshot %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "volumeId", "version", "state")); } } diff --git a/engine/schema/src/main/java/com/cloud/storage/StoragePoolAndAccessGroupMapVO.java b/engine/schema/src/main/java/com/cloud/storage/StoragePoolAndAccessGroupMapVO.java new file mode 100644 index 000000000000..5690324340c4 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/storage/StoragePoolAndAccessGroupMapVO.java @@ -0,0 +1,64 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.storage; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.cloudstack.api.InternalIdentity; + +@Entity +@Table(name = "storage_pool_and_access_group_map") +public class StoragePoolAndAccessGroupMapVO implements InternalIdentity { + + protected StoragePoolAndAccessGroupMapVO() { + } + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "pool_id") + private long poolId; + + @Column(name = "storage_access_group") + private String storageAccessGroup; + + public StoragePoolAndAccessGroupMapVO(long poolId, String storageAccessGroup) { + this.poolId = poolId; + this.storageAccessGroup = storageAccessGroup; + } + + @Override + public long getId() { + return this.id; + } + + public long getPoolId() { + return poolId; + } + + public String getStorageAccessGroup() { + return storageAccessGroup; + } + +} diff --git a/engine/schema/src/main/java/com/cloud/storage/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/VMTemplateVO.java b/engine/schema/src/main/java/com/cloud/storage/VMTemplateVO.java index 44e4dc920abf..88d3b7ba2d8d 100644 --- a/engine/schema/src/main/java/com/cloud/storage/VMTemplateVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/VMTemplateVO.java @@ -21,6 +21,7 @@ import java.util.UUID; import javax.persistence.Column; +import javax.persistence.Convert; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; @@ -31,7 +32,11 @@ import javax.persistence.TemporalType; import javax.persistence.Transient; +import com.cloud.cpu.CPU; import com.cloud.user.UserData; +import com.cloud.utils.UuidUtils; +import org.apache.cloudstack.util.CPUArchConverter; +import org.apache.cloudstack.util.HypervisorTypeConverter; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import com.cloud.hypervisor.Hypervisor.HypervisorType; @@ -108,7 +113,7 @@ public class VMTemplateVO implements VirtualMachineTemplate { private boolean crossZones = false; @Column(name = "hypervisor_type") - @Enumerated(value = EnumType.STRING) + @Convert(converter = HypervisorTypeConverter.class) private HypervisorType hypervisorType; @Column(name = "extractable") @@ -158,6 +163,9 @@ public class VMTemplateVO implements VirtualMachineTemplate { @Column(name = "deploy_as_is") private boolean deployAsIs; + @Column(name = "for_cks") + private boolean forCks; + @Column(name = "user_data_id") private Long userDataId; @@ -165,6 +173,13 @@ public class VMTemplateVO implements VirtualMachineTemplate { @Enumerated(value = EnumType.STRING) UserData.UserDataOverridePolicy userDataLinkPolicy; + @Column(name = "arch") + @Convert(converter = CPUArchConverter.class) + private CPU.CPUArch arch; + + @Column(name = "extension_id") + private Long extensionId; + @Override public String getUniqueName() { return uniqueName; @@ -207,7 +222,7 @@ private VMTemplateVO(long id, String name, ImageFormat format, boolean isPublic, public VMTemplateVO(long id, String name, ImageFormat format, boolean isPublic, boolean featured, boolean isExtractable, TemplateType type, String url, boolean requiresHvm, int bits, long accountId, String cksum, String displayText, boolean enablePassword, long guestOSId, boolean bootable, HypervisorType hyperType, String templateTag, Map details, boolean sshKeyEnabled, boolean isDynamicallyScalable, boolean directDownload, - boolean deployAsIs) { + boolean deployAsIs, CPU.CPUArch arch, Long extensionId) { this(id, name, format, @@ -233,6 +248,8 @@ public VMTemplateVO(long id, String name, ImageFormat format, boolean isPublic, state = State.Active; this.directDownload = directDownload; this.deployAsIs = deployAsIs; + this.arch = arch; + this.extensionId = extensionId; } public static VMTemplateVO createPreHostIso(Long id, String uniqueName, String name, ImageFormat format, boolean isPublic, boolean featured, TemplateType type, @@ -328,7 +345,7 @@ private static String generateUniqueName(long id, long userId, String displayNam name.append("-"); name.append(userId); name.append("-"); - name.append(UUID.nameUUIDFromBytes((displayName + System.currentTimeMillis()).getBytes()).toString()); + name.append(UuidUtils.nameUUIDFromBytes((displayName + System.currentTimeMillis()).getBytes()).toString()); return name.toString(); } @@ -563,7 +580,9 @@ public int hashCode() { @Override public String toString() { - return String.format("Template %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "uniqueName", "format")); + return String.format("Template %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "uniqueName", "format")); } public void setRemoved(Date removed) { @@ -653,6 +672,14 @@ public void setDeployAsIs(boolean deployAsIs) { this.deployAsIs = deployAsIs; } + public boolean isForCks() { + return forCks; + } + + public void setForCks(boolean forCks) { + this.forCks = forCks; + } + @Override public Long getUserDataId() { return userDataId; @@ -671,4 +698,20 @@ public void setUserDataLinkPolicy(UserData.UserDataOverridePolicy userDataLinkPo this.userDataLinkPolicy = userDataLinkPolicy; } + @Override + public CPU.CPUArch getArch() { + return arch; + } + + public void setArch(CPU.CPUArch arch) { + this.arch = arch; + } + + public Long getExtensionId() { + return extensionId; + } + + public void setExtensionId(Long extensionId) { + this.extensionId = extensionId; + } } diff --git a/engine/schema/src/main/java/com/cloud/storage/VolumeDetailVO.java b/engine/schema/src/main/java/com/cloud/storage/VolumeDetailVO.java index 6723f0b8bc18..42980e07b2bb 100644 --- a/engine/schema/src/main/java/com/cloud/storage/VolumeDetailVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/VolumeDetailVO.java @@ -80,4 +80,7 @@ public boolean isDisplay() { return display; } + public void setValue(String value) { + this.value = value; + } } 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 d8dfbb6f0762..653be54a9109 100644 --- a/engine/schema/src/main/java/com/cloud/storage/VolumeVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/VolumeVO.java @@ -48,31 +48,34 @@ public class VolumeVO implements Volume { @TableGenerator(name = "volume_sq", table = "sequence", pkColumnName = "name", valueColumnName = "value", pkColumnValue = "volume_seq", allocationSize = 1) @GeneratedValue(strategy = GenerationType.TABLE) @Column(name = "id") - long id; + private long id; + + @Column(name = "last_id") + private long lastId; @Column(name = "name") - String name; + private String name; @Column(name = "pool_id") - Long poolId; + private Long poolId; @Column(name = "last_pool_id") - Long lastPoolId; + private Long lastPoolId; @Column(name = "account_id") - long accountId; + private long accountId; @Column(name = "domain_id") - long domainId; + private long domainId; @Column(name = "instance_id") - Long instanceId = null; + private Long instanceId = null; @Column(name = "device_id") - Long deviceId = null; + private Long deviceId = null; @Column(name = "size") - Long size; + private Long size; @Column(name = "min_iops") private Long minIops; @@ -81,50 +84,50 @@ public class VolumeVO implements Volume { private Long maxIops; @Column(name = "folder") - String folder; + private String folder; @Column(name = "path") - String path; + private String path; @Column(name = "pod_id") - Long podId; + private Long podId; @Column(name = "created") - Date created; + private Date created; @Column(name = "attached") @Temporal(value = TemporalType.TIMESTAMP) - Date attached; + private Date attached; @Column(name = "data_center_id") - long dataCenterId; + private long dataCenterId; @Column(name = "host_ip") - String hostip; + private String hostIp; @Column(name = "disk_offering_id") - long diskOfferingId; + private long diskOfferingId; @Column(name = "template_id") - Long templateId; + private Long templateId; @Column(name = "first_snapshot_backup_uuid") - String firstSnapshotBackupUuid; + private String firstSnapshotBackupUuid; @Column(name = "volume_type") @Enumerated(EnumType.STRING) - Type volumeType = Volume.Type.UNKNOWN; + private Type volumeType = Volume.Type.UNKNOWN; @Column(name = "pool_type") @Convert(converter = StoragePoolTypeConverter.class) - StoragePoolType poolType; + private StoragePoolType poolType; @Column(name = GenericDao.REMOVED_COLUMN) - Date removed; + private Date removed; @Column(name = "updated") @Temporal(value = TemporalType.TIMESTAMP) - Date updated; + private Date updated; @Column(name = "update_count", updatable = true, nullable = false) protected long updatedCount; // This field should be updated everytime the @@ -133,17 +136,17 @@ public class VolumeVO implements Volume { // dao code. @Column(name = "recreatable") - boolean recreatable; + private boolean recreatable; @Column(name = "state") @Enumerated(value = EnumType.STRING) private State state; @Column(name = "chain_info", length = 65535) - String chainInfo; + private String chainInfo; @Column(name = "uuid") - String uuid; + private String uuid; @Column(name = "format") private Storage.ImageFormat format; @@ -168,7 +171,7 @@ public class VolumeVO implements Volume { @Transient // @Column(name="reservation") - String reservationId; + private String reservationId; @Column(name = "hv_ss_reserve") private Integer hypervisorSnapshotReserve; @@ -182,6 +185,10 @@ public class VolumeVO implements Volume { @Column(name = "encrypt_format") private String encryptFormat; + @Column(name = "delete_protection") + private boolean deleteProtection; + + // Real Constructor public VolumeVO(Type type, String name, long dcId, long domainId, long accountId, long diskOfferingId, Storage.ProvisioningType provisioningType, long size, @@ -424,11 +431,11 @@ public void setPath(String path) { } public String getHostIp() { - return hostip; + return hostIp; } public void setHostIp(String hostip) { - this.hostip = hostip; + this.hostIp = hostip; } public void setPodId(Long podId) { @@ -507,7 +514,9 @@ public void setUpdated(Date updated) { @Override public String toString() { - return new StringBuilder("Vol[").append(id).append("|name=").append(name).append("|vm=").append(instanceId).append("|").append(volumeType).append("]").toString(); + return String.format("Volume %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "volumeType", "instanceId")); } @Override @@ -677,4 +686,21 @@ public void setExternalUuid(String externalUuid) { public String getEncryptFormat() { return encryptFormat; } public void setEncryptFormat(String encryptFormat) { this.encryptFormat = encryptFormat; } + + @Override + public boolean isDeleteProtection() { + return deleteProtection; + } + + public void setDeleteProtection(boolean deleteProtection) { + this.deleteProtection = deleteProtection; + } + + public long getLastId() { + return lastId; + } + + public void setLastId(long lastId) { + this.lastId = lastId; + } } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/BucketDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/BucketDao.java index f45f28b5c2c2..2511df49807e 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/BucketDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/BucketDao.java @@ -27,4 +27,8 @@ public interface BucketDao extends GenericDao { List listByObjectStoreIdAndAccountId(long objectStoreId, long accountId); List searchByIds(Long[] ids); + + Long countBucketsForAccount(long accountId); + + Long calculateObjectStorageAllocationForAccount(long accountId); } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/BucketDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/BucketDaoImpl.java index 98bef6201a15..473879d933dc 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/BucketDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/BucketDaoImpl.java @@ -16,8 +16,10 @@ // under the License. package com.cloud.storage.dao; +import com.cloud.configuration.Resource; import com.cloud.storage.BucketVO; 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 org.springframework.stereotype.Component; @@ -31,6 +33,8 @@ public class BucketDaoImpl extends GenericDaoBase implements Buc private SearchBuilder searchFilteringStoreId; private SearchBuilder bucketSearch; + private GenericSearchBuilder CountBucketsByAccount; + private GenericSearchBuilder CalculateBucketsQuotaByAccount; private static final String STORE_ID = "store_id"; private static final String STATE = "state"; @@ -54,6 +58,20 @@ public boolean configure(String name, Map params) throws Configu bucketSearch.and("idIN", bucketSearch.entity().getId(), SearchCriteria.Op.IN); bucketSearch.done(); + CountBucketsByAccount = createSearchBuilder(Long.class); + CountBucketsByAccount.select(null, SearchCriteria.Func.COUNT, null); + CountBucketsByAccount.and(ACCOUNT_ID, CountBucketsByAccount.entity().getAccountId(), SearchCriteria.Op.EQ); + CountBucketsByAccount.and(STATE, CountBucketsByAccount.entity().getState(), SearchCriteria.Op.NIN); + CountBucketsByAccount.and("removed", CountBucketsByAccount.entity().getRemoved(), SearchCriteria.Op.NULL); + CountBucketsByAccount.done(); + + CalculateBucketsQuotaByAccount = createSearchBuilder(SumCount.class); + CalculateBucketsQuotaByAccount.select("sum", SearchCriteria.Func.SUM, CalculateBucketsQuotaByAccount.entity().getQuota()); + CalculateBucketsQuotaByAccount.and(ACCOUNT_ID, CalculateBucketsQuotaByAccount.entity().getAccountId(), SearchCriteria.Op.EQ); + CalculateBucketsQuotaByAccount.and(STATE, CalculateBucketsQuotaByAccount.entity().getState(), SearchCriteria.Op.NIN); + CalculateBucketsQuotaByAccount.and("removed", CalculateBucketsQuotaByAccount.entity().getRemoved(), SearchCriteria.Op.NULL); + CalculateBucketsQuotaByAccount.done(); + return true; } @Override @@ -79,4 +97,21 @@ public List searchByIds(Long[] ids) { sc.setParameters("idIN", ids); return search(sc, null, null, false); } + + @Override + public Long countBucketsForAccount(long accountId) { + SearchCriteria sc = CountBucketsByAccount.create(); + sc.setParameters(ACCOUNT_ID, accountId); + sc.setParameters(STATE, BucketVO.State.Destroyed); + return customSearch(sc, null).get(0); + } + + @Override + public Long calculateObjectStorageAllocationForAccount(long accountId) { + SearchCriteria sc = CalculateBucketsQuotaByAccount.create(); + sc.setParameters(ACCOUNT_ID, accountId); + sc.setParameters(STATE, BucketVO.State.Destroyed); + Long totalQuota = customSearch(sc, null).get(0).sum; + return (totalQuota * Resource.ResourceType.bytesToGiB); + } } 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 5a49d0b5192f..9beea0037444 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 @@ -32,4 +32,7 @@ public interface DiskOfferingDao extends GenericDao { List findCustomDiskOfferings(); + 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 78b2a542837f..4ca3fe9f12ac 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 @@ -26,13 +26,13 @@ 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; @@ -46,6 +46,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"; @@ -57,17 +59,12 @@ protected DiskOfferingDaoImpl() { UniqueNameSearch.and("name", UniqueNameSearch.entity().getUniqueName(), SearchCriteria.Op.EQ); UniqueNameSearch.done(); - _computeOnlyAttr = _allAttributes.get("computeOnly"); - } - - @Override - public List searchIncludingRemoved(SearchCriteria sc, final Filter filter, final Boolean lock, final boolean cache) { - return super.searchIncludingRemoved(sc, filter, lock, cache); - } + ActiveAndNonComputeSearch = createSearchBuilder(); + ActiveAndNonComputeSearch.and("state", ActiveAndNonComputeSearch.entity().getState(), SearchCriteria.Op.EQ); + ActiveAndNonComputeSearch.and("computeOnly", ActiveAndNonComputeSearch.entity().isComputeOnly(), SearchCriteria.Op.EQ); + ActiveAndNonComputeSearch.done(); - @Override - public List customSearchIncludingRemoved(SearchCriteria sc, final Filter filter) { - return super.customSearchIncludingRemoved(sc, filter); + _computeOnlyAttr = _allAttributes.get("computeOnly"); } @Override @@ -157,4 +154,30 @@ public boolean remove(Long id) { return update(id, diskOffering); } + + @Override + public List listByStorageTag(String tag) { + SearchBuilder sb = createSearchBuilder(); + sb.and("tagNotNull", sb.entity().getTags(), SearchCriteria.Op.NNULL); + sb.and().op("tagEq", sb.entity().getTags(), SearchCriteria.Op.EQ); + sb.or("tagStartLike", sb.entity().getTags(), SearchCriteria.Op.LIKE); + sb.or("tagMidLike", sb.entity().getTags(), SearchCriteria.Op.LIKE); + sb.or("tagEndLike", sb.entity().getTags(), SearchCriteria.Op.LIKE); + sb.cp(); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("tagEq", tag); + sc.setParameters("tagStartLike", tag + ",%"); + sc.setParameters("tagMidLike", "%," + 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/GuestOSCategoryDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSCategoryDaoImpl.java index 6fad6c5c47ee..cc1c96aee52c 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSCategoryDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSCategoryDaoImpl.java @@ -27,7 +27,6 @@ public class GuestOSCategoryDaoImpl extends GenericDaoBase implements GuestOSCategoryDao { protected GuestOSCategoryDaoImpl() { - } @Override diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDao.java index 13cd398073ad..1a2b098c40a7 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDao.java @@ -16,14 +16,14 @@ // under the License. package com.cloud.storage.dao; +import java.util.List; +import java.util.Set; + import com.cloud.storage.GuestOS; import com.cloud.storage.GuestOSVO; import com.cloud.utils.Pair; import com.cloud.utils.db.GenericDao; -import java.util.List; -import java.util.Set; - public interface GuestOSDao extends GenericDao { GuestOSVO findOneByDisplayName(String displayName); @@ -36,4 +36,6 @@ public interface GuestOSDao extends GenericDao { List listByDisplayName(String displayName); Pair, Integer> listGuestOSByCriteria(Long startIndex, Long pageSize, Long id, Long osCategoryId, String description, String keyword, Boolean forDisplay); + + List listIdsByCategoryId(final long categoryId); } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDaoImpl.java index efcaa482a676..881be207c1aa 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDaoImpl.java @@ -25,19 +25,20 @@ import java.util.List; import java.util.Set; -import com.cloud.storage.GuestOS; -import com.cloud.utils.Pair; -import com.cloud.utils.db.DB; -import com.cloud.utils.db.TransactionLegacy; -import com.cloud.utils.exception.CloudRuntimeException; import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; +import com.cloud.storage.GuestOS; import com.cloud.storage.GuestOSVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.TransactionLegacy; +import com.cloud.utils.exception.CloudRuntimeException; @Component public class GuestOSDaoImpl extends GenericDaoBase implements GuestOSDao { @@ -152,4 +153,14 @@ public Pair, Integer> listGuestOSByCriteria(Long startIn return new Pair<>(result.first(), result.second()); } + @Override + public List listIdsByCategoryId(final long categoryId) { + GenericSearchBuilder sb = createSearchBuilder(Long.class); + sb.selectFields(sb.entity().getId()); + sb.and("categoryId", sb.entity().getCategoryId(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("categoryId", categoryId); + return customSearch(sc, null); + } } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/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 998d0bbd724c..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); @@ -57,4 +59,7 @@ public interface SnapshotDao extends GenericDao, StateDao listByIds(Object... ids); + List searchByVolumes(List volumeIds); + + List listByVolumeIdAndTypeNotInAndStateNotRemoved(long volumeId, Type... type); } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDaoImpl.java index 030d10d66827..80e1b7d4d4be 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 @@ -18,11 +18,14 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import javax.annotation.PostConstruct; import javax.inject.Inject; +import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; import com.cloud.server.ResourceTag.ResourceObjectType; @@ -54,6 +57,10 @@ public class SnapshotDaoImpl extends GenericDaoBase implements private static final String GET_LAST_SNAPSHOT = "SELECT snapshots.id FROM snapshot_store_ref, snapshots where snapshots.id = snapshot_store_ref.snapshot_id AND snapshosts.volume_id = ? AND snapshot_store_ref.role = ? ORDER BY created DESC"; + private static final String VOLUME_ID = "volumeId"; + private static final String NOT_TYPE = "notType"; + private static final String STATUS = "status"; + private SearchBuilder snapshotIdsSearch; private SearchBuilder VolumeIdSearch; private SearchBuilder VolumeIdTypeSearch; @@ -64,6 +71,8 @@ public class SnapshotDaoImpl extends GenericDaoBase implements private SearchBuilder StatusSearch; private SearchBuilder notInStatusSearch; private GenericSearchBuilder CountSnapshotsByAccount; + + private SearchBuilder volumeIdAndTypeNotInSearch; @Inject ResourceTagDao _tagsDao; @Inject @@ -179,6 +188,12 @@ protected void init() { InstanceIdSearch.join("instanceSnapshots", volumeSearch, volumeSearch.entity().getId(), InstanceIdSearch.entity().getVolumeId(), JoinType.INNER); InstanceIdSearch.done(); + + volumeIdAndTypeNotInSearch = createSearchBuilder(); + volumeIdAndTypeNotInSearch.and(VOLUME_ID, volumeIdAndTypeNotInSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); + volumeIdAndTypeNotInSearch.and(STATUS, volumeIdAndTypeNotInSearch.entity().getState(), SearchCriteria.Op.NEQ); + volumeIdAndTypeNotInSearch.and(NOT_TYPE, volumeIdAndTypeNotInSearch.entity().getTypeDescription(), SearchCriteria.Op.NOTIN); + volumeIdAndTypeNotInSearch.done(); } @Override @@ -250,6 +265,13 @@ public List listAllByStatus(Snapshot.State... 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(); @@ -285,4 +307,26 @@ public List listByStatusNotIn(long volumeId, Snapshot.State... statu sc.setParameters("status", (Object[]) status); return listBy(sc, null); } + + @Override + public List searchByVolumes(List volumeIds) { + if (CollectionUtils.isEmpty(volumeIds)) { + return new ArrayList<>(); + } + SearchBuilder sb = createSearchBuilder(); + sb.and("volumeIds", sb.entity().getVolumeId(), SearchCriteria.Op.IN); + SearchCriteria sc = sb.create(); + sc.setParameters("volumeIds", volumeIds.toArray()); + return search(sc, null); + } + + @Override + public List listByVolumeIdAndTypeNotInAndStateNotRemoved(long volumeId, Type... types) { + SearchCriteria sc = volumeIdAndTypeNotInSearch.create(); + sc.setParameters(VOLUME_ID, volumeId); + sc.setParameters(NOT_TYPE, Arrays.stream(types).map(Type::toString).toArray()); + sc.setParameters(STATUS, State.Destroyed); + + return listBy(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDetailsDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDetailsDao.java index 43bb5b3d4d53..02a0355d92dd 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDetailsDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDetailsDao.java @@ -18,9 +18,12 @@ */ package com.cloud.storage.dao; +import java.util.List; + import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; import com.cloud.utils.db.GenericDao; public interface SnapshotDetailsDao extends GenericDao, ResourceDetailsDao { + public List findDetailsByZoneAndKey(long dcId, String key); } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDetailsDaoImpl.java index e4ae22cd021a..584a24817264 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDetailsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDetailsDaoImpl.java @@ -18,11 +18,44 @@ */ package com.cloud.storage.dao; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; +import com.cloud.utils.db.TransactionLegacy; +import com.cloud.utils.exception.CloudRuntimeException; + public class SnapshotDetailsDaoImpl extends ResourceDetailsDaoBase implements SnapshotDetailsDao { + private static final String GET_SNAPSHOT_DETAILS_ON_ZONE = "SELECT s.* FROM snapshot_details s LEFT JOIN snapshots ss ON ss.id=s.snapshot_id WHERE ss.data_center_id = ? AND s.name = ?"; + @Override public void addDetail(long resourceId, String key, String value, boolean display) { super.addDetail(new SnapshotDetailsVO(resourceId, key, value, display)); } + + public List findDetailsByZoneAndKey(long dcId, String key) { + StringBuilder sql = new StringBuilder(GET_SNAPSHOT_DETAILS_ON_ZONE); + TransactionLegacy txn = TransactionLegacy.currentTxn(); + List snapshotDetailsOnZone = new ArrayList(); + try (PreparedStatement pstmt = txn.prepareStatement(sql.toString());) { + if (pstmt != null) { + pstmt.setLong(1, dcId); + pstmt.setString(2, key); + try (ResultSet rs = pstmt.executeQuery();) { + while (rs.next()) { + snapshotDetailsOnZone.add(toEntityBean(rs, false)); + } + } catch (SQLException e) { + throw new CloudRuntimeException("Could not find details by given zone and key due to:" + e.getMessage(), e); + } + } + return snapshotDetailsOnZone; + } catch (SQLException e) { + throw new CloudRuntimeException("Could not find details by given zone and key due to:" + e.getMessage(), e); + } + } } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotScheduleDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotScheduleDao.java index 7ca0a3915f54..284a42cf9e1d 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotScheduleDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotScheduleDao.java @@ -27,13 +27,11 @@ */ public interface SnapshotScheduleDao extends GenericDao { - List getCoincidingSnapshotSchedules(long volumeId, Date date); - List getSchedulesToExecute(Date currentTimestamp); - SnapshotScheduleVO getCurrentSchedule(Long volumeId, Long policyId, boolean executing); + List getSchedulesAssignedWithAsyncJob(); - SnapshotScheduleVO findOneByVolume(long volumeId); + SnapshotScheduleVO getCurrentSchedule(Long volumeId, Long policyId, boolean executing); SnapshotScheduleVO findOneByVolumePolicy(long volumeId, long policyId); diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotScheduleDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotScheduleDaoImpl.java index 925d02dd90b4..14669ce1d438 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotScheduleDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotScheduleDaoImpl.java @@ -32,7 +32,7 @@ public class SnapshotScheduleDaoImpl extends GenericDaoBase implements SnapshotScheduleDao { protected final SearchBuilder executableSchedulesSearch; protected final SearchBuilder coincidingSchedulesSearch; - private final SearchBuilder VolumeIdSearch; + protected final SearchBuilder schedulesAssignedWithAsyncJob; private final SearchBuilder VolumeIdPolicyIdSearch; protected SnapshotScheduleDaoImpl() { @@ -48,36 +48,14 @@ protected SnapshotScheduleDaoImpl() { coincidingSchedulesSearch.and("asyncJobId", coincidingSchedulesSearch.entity().getAsyncJobId(), SearchCriteria.Op.NULL); coincidingSchedulesSearch.done(); - VolumeIdSearch = createSearchBuilder(); - VolumeIdSearch.and("volumeId", VolumeIdSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); - VolumeIdSearch.done(); - VolumeIdPolicyIdSearch = createSearchBuilder(); VolumeIdPolicyIdSearch.and("volumeId", VolumeIdPolicyIdSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); VolumeIdPolicyIdSearch.and("policyId", VolumeIdPolicyIdSearch.entity().getPolicyId(), SearchCriteria.Op.EQ); VolumeIdPolicyIdSearch.done(); - } - - /** - * {@inheritDoc} - */ - @Override - public List getCoincidingSnapshotSchedules(long volumeId, Date date) { - SearchCriteria sc = coincidingSchedulesSearch.create(); - sc.setParameters("volumeId", volumeId); - sc.setParameters("scheduledTimestamp", date); - // Don't return manual snapshots. They will be executed through another - // code path. - sc.addAnd("policyId", SearchCriteria.Op.NEQ, 1L); - return listBy(sc); - } - - @Override - public SnapshotScheduleVO findOneByVolume(long volumeId) { - SearchCriteria sc = VolumeIdSearch.create(); - sc.setParameters("volumeId", volumeId); - return findOneBy(sc); + schedulesAssignedWithAsyncJob = createSearchBuilder(); + schedulesAssignedWithAsyncJob.and("asyncJobId", schedulesAssignedWithAsyncJob.entity().getAsyncJobId(), SearchCriteria.Op.NNULL); + schedulesAssignedWithAsyncJob.done(); } @Override @@ -98,6 +76,11 @@ public List getSchedulesToExecute(Date currentTimestamp) { return listBy(sc); } + @Override + public List getSchedulesAssignedWithAsyncJob() { + return listBy(schedulesAssignedWithAsyncJob.create()); + } + /** * {@inheritDoc} */ diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolAndAccessGroupMapDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolAndAccessGroupMapDao.java new file mode 100644 index 000000000000..3ff797f7e741 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolAndAccessGroupMapDao.java @@ -0,0 +1,31 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.storage.dao; + +import java.util.List; + +import com.cloud.storage.StoragePoolAndAccessGroupMapVO; + +import com.cloud.utils.db.GenericDao; + +public interface StoragePoolAndAccessGroupMapDao extends GenericDao { + + void persist(long poolId, List storageAccessGroups); + List getStorageAccessGroups(long poolId); + void deleteStorageAccessGroups(long poolId); + List listDistinctStorageAccessGroups(String name, String keyword); +} diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolAndAccessGroupMapDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolAndAccessGroupMapDaoImpl.java new file mode 100644 index 000000000000..63e82b79748d --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolAndAccessGroupMapDaoImpl.java @@ -0,0 +1,105 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.storage.dao; + +import java.util.ArrayList; +import java.util.List; + +import com.cloud.storage.StoragePoolAndAccessGroupMapVO; + +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.TransactionLegacy; + +public class StoragePoolAndAccessGroupMapDaoImpl extends GenericDaoBase implements StoragePoolAndAccessGroupMapDao { + + protected final SearchBuilder StoragePoolAccessGroupSearch; + + public StoragePoolAndAccessGroupMapDaoImpl() { + StoragePoolAccessGroupSearch = createSearchBuilder(); + StoragePoolAccessGroupSearch.and("poolId", StoragePoolAccessGroupSearch.entity().getPoolId(), SearchCriteria.Op.EQ); + StoragePoolAccessGroupSearch.done(); + } + + @Override + public void persist(long poolId, List storageAccessGroups) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + + txn.start(); + SearchCriteria sc = StoragePoolAccessGroupSearch.create(); + sc.setParameters("poolId", poolId); + expunge(sc); + + for (String sag : storageAccessGroups) { + sag = sag.trim(); + if (sag.length() > 0) { + StoragePoolAndAccessGroupMapVO vo = new StoragePoolAndAccessGroupMapVO(poolId, sag); + persist(vo); + } + } + txn.commit(); + } + + @Override + public List getStorageAccessGroups(long poolId) { + SearchCriteria sc = StoragePoolAccessGroupSearch.create(); + sc.setParameters("poolId", poolId); + + List results = search(sc, null); + List storagePoolAccessGroups = new ArrayList(results.size()); + for (StoragePoolAndAccessGroupMapVO result : results) { + storagePoolAccessGroups.add(result.getStorageAccessGroup()); + } + + return storagePoolAccessGroups; + } + + @Override + public void deleteStorageAccessGroups(long poolId) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + txn.start(); + SearchCriteria sc = StoragePoolAccessGroupSearch.create(); + sc.setParameters("poolId", poolId); + expunge(sc); + txn.commit(); + } + + @Override + public List listDistinctStorageAccessGroups(String name, String keyword) { + GenericSearchBuilder searchBuilder = createSearchBuilder(String.class); + + searchBuilder.select(null, SearchCriteria.Func.DISTINCT, searchBuilder.entity().getStorageAccessGroup()); + searchBuilder.and("name", searchBuilder.entity().getStorageAccessGroup(), SearchCriteria.Op.EQ); + searchBuilder.and("keyword", searchBuilder.entity().getStorageAccessGroup(), SearchCriteria.Op.LIKE); + searchBuilder.done(); + + SearchCriteria sc = searchBuilder.create(); + + if (name != null) { + sc.setParameters("name", name); + } + + if (keyword != null) { + sc.setParameters("keyword", "%" + keyword + "%"); + } + + return customSearch(sc, null); + } + +} diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolDetailsDaoImpl.java index 0c39a8c581aa..a3baa3b4cb06 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolDetailsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolDetailsDaoImpl.java @@ -17,6 +17,10 @@ package com.cloud.storage.dao; +import java.util.List; + +import javax.inject.Inject; + import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.ConfigKey.Scope; import org.apache.cloudstack.framework.config.ScopedConfigStorage; @@ -26,8 +30,7 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; -import javax.inject.Inject; -import java.util.List; +import com.cloud.utils.Pair; public class StoragePoolDetailsDaoImpl extends ResourceDetailsDaoBase implements StoragePoolDetailsDao, ScopedConfigStorage { @@ -43,9 +46,9 @@ public Scope getScope() { } @Override - public String getConfigValue(long id, ConfigKey key) { - StoragePoolDetailVO vo = findDetail(id, key.key()); - return vo == null ? null : vo.getValue(); + public String getConfigValue(long id, String key) { + StoragePoolDetailVO vo = findDetail(id, key); + return vo == null ? null : getActualValue(vo); } @Override @@ -56,4 +59,17 @@ public void addDetail(long resourceId, String key, String value, boolean display } super.addDetail(new StoragePoolDetailVO(resourceId, key, value, display)); } + + @Override + public Pair getParentScope(long id) { + StoragePoolVO pool = _storagePoolDao.findById(id); + if (pool != null) { + if (pool.getClusterId() != null) { + return new Pair<>(getScope().getParent(), pool.getClusterId()); + } else { + return new Pair<>(ConfigKey.Scope.Zone, pool.getDataCenterId()); + } + } + return null; + } } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolHostDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolHostDao.java index b099a6d6bdbb..94e13ba2d56e 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolHostDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolHostDao.java @@ -30,15 +30,19 @@ public interface StoragePoolHostDao extends GenericDao public StoragePoolHostVO findByPoolHost(long poolId, long hostId); + List findByLocalPath(String path); + List listByHostStatus(long poolId, Status hostStatus); List findHostsConnectedToPools(List poolIds); - List> getDatacenterStoragePoolHostInfo(long dcId, boolean sharedOnly); + boolean hasDatacenterStoragePoolHostInfo(long dcId, boolean sharedOnly); public void deletePrimaryRecordsForHost(long hostId); public void deleteStoragePoolHostDetails(long hostId, long poolId); List listByHostId(long hostId); + + Pair, Integer> listByPoolIdNotInCluster(long clusterId, long poolId); } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolHostDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolHostDaoImpl.java index 9e7bdca11817..55b5668bbc17 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolHostDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolHostDaoImpl.java @@ -23,12 +23,18 @@ import java.util.List; import java.util.stream.Collectors; +import javax.annotation.PostConstruct; +import javax.inject.Inject; + import org.springframework.stereotype.Component; +import com.cloud.host.HostVO; import com.cloud.host.Status; +import com.cloud.host.dao.HostDao; import com.cloud.storage.StoragePoolHostVO; import com.cloud.utils.Pair; import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.JoinBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.TransactionLegacy; @@ -39,16 +45,22 @@ public class StoragePoolHostDaoImpl extends GenericDaoBase PoolSearch; protected final SearchBuilder HostSearch; protected final SearchBuilder PoolHostSearch; + protected final SearchBuilder LocalPathSearch; + + protected SearchBuilder poolNotInClusterSearch; + + @Inject + HostDao hostDao; protected static final String HOST_FOR_POOL_SEARCH = "SELECT * FROM storage_pool_host_ref ph, host h where ph.host_id = h.id and ph.pool_id=? and h.status=? "; protected static final String HOSTS_FOR_POOLS_SEARCH = "SELECT DISTINCT(ph.host_id) FROM storage_pool_host_ref ph, host h WHERE ph.host_id = h.id AND h.status = 'Up' AND resource_state = 'Enabled' AND ph.pool_id IN (?)"; - protected static final String STORAGE_POOL_HOST_INFO = "SELECT p.data_center_id, count(ph.host_id) " + " FROM storage_pool p, storage_pool_host_ref ph " - + " WHERE p.id = ph.pool_id AND p.data_center_id = ? " + " GROUP by p.data_center_id"; + protected static final String STORAGE_POOL_HOST_INFO = "SELECT (SELECT id FROM storage_pool_host_ref ph WHERE " + + "ph.pool_id=p.id limit 1) AS sphr FROM storage_pool p WHERE p.data_center_id = ?"; - protected static final String SHARED_STORAGE_POOL_HOST_INFO = "SELECT p.data_center_id, count(ph.host_id) " + " FROM storage_pool p, storage_pool_host_ref ph " - + " WHERE p.id = ph.pool_id AND p.data_center_id = ? " + " AND p.pool_type NOT IN ('LVM', 'Filesystem')" + " GROUP by p.data_center_id"; + protected static final String SHARED_STORAGE_POOL_HOST_INFO = "SELECT (SELECT id FROM storage_pool_host_ref ph " + + "WHERE ph.pool_id=p.id limit 1) AS sphr FROM storage_pool p WHERE p.data_center_id = ? AND p.pool_type NOT IN ('LVM', 'Filesystem')"; protected static final String DELETE_PRIMARY_RECORDS = "DELETE " + "FROM storage_pool_host_ref " + "WHERE host_id = ?"; @@ -66,6 +78,18 @@ public StoragePoolHostDaoImpl() { PoolHostSearch.and("host_id", PoolHostSearch.entity().getHostId(), SearchCriteria.Op.EQ); PoolHostSearch.done(); + LocalPathSearch = createSearchBuilder(); + LocalPathSearch.and("local_path", LocalPathSearch.entity().getLocalPath(), SearchCriteria.Op.EQ); + LocalPathSearch.done(); + } + + @PostConstruct + public void init(){ + poolNotInClusterSearch = createSearchBuilder(); + poolNotInClusterSearch.and("poolId", poolNotInClusterSearch.entity().getPoolId(), SearchCriteria.Op.EQ); + SearchBuilder hostSearch = hostDao.createSearchBuilder(); + poolNotInClusterSearch.join("hostSearch", hostSearch, hostSearch.entity().getId(), poolNotInClusterSearch.entity().getHostId(), JoinBuilder.JoinType.INNER); + hostSearch.and("clusterId", hostSearch.entity().getClusterId(), SearchCriteria.Op.NEQ); } @Override @@ -97,6 +121,13 @@ public StoragePoolHostVO findByPoolHost(long poolId, long hostId) { return findOneIncludingRemovedBy(sc); } + @Override + public List findByLocalPath(String path) { + SearchCriteria sc = LocalPathSearch.create(); + sc.setParameters("local_path", path); + return listBy(sc); + } + @Override public List listByHostStatus(long poolId, Status hostStatus) { TransactionLegacy txn = TransactionLegacy.currentTxn(); @@ -149,23 +180,23 @@ public List findHostsConnectedToPools(List poolIds) { } @Override - public List> getDatacenterStoragePoolHostInfo(long dcId, boolean sharedOnly) { - ArrayList> l = new ArrayList>(); + public boolean hasDatacenterStoragePoolHostInfo(long dcId, boolean sharedOnly) { + Long poolCount = 0L; String sql = sharedOnly ? SHARED_STORAGE_POOL_HOST_INFO : STORAGE_POOL_HOST_INFO; TransactionLegacy txn = TransactionLegacy.currentTxn(); - PreparedStatement pstmt = null; - try { - pstmt = txn.prepareAutoCloseStatement(sql); + try (PreparedStatement pstmt = txn.prepareAutoCloseStatement(sql)) { pstmt.setLong(1, dcId); - ResultSet rs = pstmt.executeQuery(); while (rs.next()) { - l.add(new Pair(rs.getLong(1), rs.getInt(2))); + poolCount = rs.getLong(1); + if (poolCount > 0) { + return true; + } } } catch (SQLException e) { logger.debug("SQLException: ", e); } - return l; + return false; } /** @@ -194,4 +225,12 @@ public void deleteStoragePoolHostDetails(long hostId, long poolId) { remove(sc); txn.commit(); } + + @Override + public Pair, Integer> listByPoolIdNotInCluster(long clusterId, long poolId) { + SearchCriteria sc = poolNotInClusterSearch.create(); + sc.setParameters("poolId", poolId); + sc.setJoinParameters("hostSearch", "clusterId", clusterId); + return searchAndCount(sc, null); + } } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolTagsDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolTagsDao.java index 9352ee21858c..a4b87ef025f3 100755 --- a/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolTagsDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolTagsDao.java @@ -34,5 +34,6 @@ public interface StoragePoolTagsDao extends GenericDao { StorageTagResponse newStorageTagResponse(StoragePoolTagVO tag); List findStoragePoolTags(long poolId); + List listPoolIdsByTag(String tag); } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolTagsDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolTagsDaoImpl.java index c01c66763af1..c4d7ed886072 100755 --- a/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolTagsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolTagsDaoImpl.java @@ -18,12 +18,10 @@ import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; import javax.inject.Inject; -import com.cloud.utils.db.Transaction; -import com.cloud.utils.db.TransactionCallbackNoReturn; -import com.cloud.utils.db.TransactionStatus; import org.apache.cloudstack.api.response.StorageTagResponse; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; @@ -31,7 +29,10 @@ import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallbackNoReturn; import com.cloud.utils.db.TransactionLegacy; +import com.cloud.utils.db.TransactionStatus; public class StoragePoolTagsDaoImpl extends GenericDaoBase implements StoragePoolTagsDao { @@ -178,4 +179,15 @@ public List findStoragePoolTags(long poolId) { return search(sc, null); } + @Override + public List listPoolIdsByTag(String tag) { + SearchBuilder sb = createSearchBuilder(); + sb.and("tag", sb.entity().getTag(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("tag", tag); + List poolRefs = search(sc, null); + return poolRefs.stream().map(StoragePoolTagVO::getPoolId).collect(Collectors.toList()); + } + } 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 708a77a8f9ec..4c9f906b68a9 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDao.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Map; +import com.cloud.cpu.CPU; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.Storage; import com.cloud.storage.VMTemplateVO; @@ -57,6 +58,8 @@ public interface VMTemplateDao extends GenericDao, StateDao< public List listInZoneByState(long dataCenterId, VirtualMachineTemplate.State... states); + public List listTemplateIsoByArchVnfAndZone(Long dataCenterId, CPU.CPUArch arch, Boolean isIso, Boolean isVnf); + public List listAllActive(); public List listByState(VirtualMachineTemplate.State... states); @@ -67,13 +70,19 @@ public interface VMTemplateDao extends GenericDao, StateDao< public List userIsoSearch(boolean listRemoved); + List listAllReadySystemVMTemplates(Long zoneId); + VMTemplateVO findSystemVMTemplate(long zoneId); - VMTemplateVO findSystemVMReadyTemplate(long zoneId, HypervisorType hypervisorType); + VMTemplateVO findSystemVMReadyTemplate(long zoneId, HypervisorType hypervisorType, String preferredArch); + + List findSystemVMReadyTemplates(long zoneId, HypervisorType hypervisorType, String preferredArch); VMTemplateVO findRoutingTemplate(HypervisorType type, String templateName); - VMTemplateVO findLatestTemplateByTypeAndHypervisor(HypervisorType hypervisorType, Storage.TemplateType type); + List findRoutingTemplates(HypervisorType type, String templateName, String preferredArch); + + VMTemplateVO findLatestTemplateByTypeAndHypervisorAndArch(HypervisorType hypervisorType, CPU.CPUArch arch, Storage.TemplateType type); public Long countTemplatesForAccount(long accountId); @@ -85,9 +94,16 @@ public interface VMTemplateDao extends GenericDao, StateDao< List listByParentTemplatetId(long parentTemplatetId); - VMTemplateVO findLatestTemplateByName(String name); + VMTemplateVO findLatestTemplateByName(String name, HypervisorType hypervisorType, CPU.CPUArch arch); List findTemplatesLinkedToUserdata(long userdataId); List listByIds(List ids); + + List listIdsByTemplateTag(String tag); + + List listIdsByExtensionId(long extensionId); + + VMTemplateVO findActiveSystemTemplateByHypervisorArchAndUrlPath(HypervisorType hypervisorType, + CPU.CPUArch arch, String urlPathSuffix); } 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 03678732d1c6..9b5d0edc599d 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDaoImpl.java @@ -17,10 +17,14 @@ package com.cloud.storage.dao; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import javax.inject.Inject; import javax.naming.ConfigurationException; @@ -28,8 +32,10 @@ import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; +import com.cloud.cpu.CPU; import com.cloud.dc.dao.DataCenterDao; import com.cloud.domain.dao.DomainDao; import com.cloud.host.Host; @@ -47,6 +53,7 @@ import com.cloud.tags.ResourceTagVO; import com.cloud.tags.dao.ResourceTagDao; import com.cloud.template.VirtualMachineTemplate; +import com.cloud.utils.Pair; import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; @@ -93,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; @@ -111,6 +117,7 @@ public VMTemplateDaoImpl() { LatestTemplateByHypervisorTypeSearch = createSearchBuilder(); LatestTemplateByHypervisorTypeSearch.and("hypervisorType", LatestTemplateByHypervisorTypeSearch.entity().getHypervisorType(), SearchCriteria.Op.EQ); LatestTemplateByHypervisorTypeSearch.and("templateType", LatestTemplateByHypervisorTypeSearch.entity().getTemplateType(), SearchCriteria.Op.EQ); + LatestTemplateByHypervisorTypeSearch.and("arch", LatestTemplateByHypervisorTypeSearch.entity().getArch(), SearchCriteria.Op.EQ); LatestTemplateByHypervisorTypeSearch.and("removed", LatestTemplateByHypervisorTypeSearch.entity().getRemoved(), SearchCriteria.Op.NULL); } @@ -238,10 +245,20 @@ public List listReadyTemplates() { @Override - public VMTemplateVO findLatestTemplateByName(String name) { - SearchCriteria sc = createSearchCriteria(); - sc.addAnd("name", SearchCriteria.Op.EQ, name); - sc.addAnd("removed", SearchCriteria.Op.NULL); + public VMTemplateVO findLatestTemplateByName(String name, 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); + } Filter filter = new Filter(VMTemplateVO.class, "id", false, null, 1L); List templates = listBy(sc, filter); if ((templates != null) && !templates.isEmpty()) { @@ -301,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(); @@ -344,19 +361,12 @@ public boolean configure(String name, Map params) throws Configu readySystemTemplateSearch = createSearchBuilder(); readySystemTemplateSearch.and("state", readySystemTemplateSearch.entity().getState(), SearchCriteria.Op.EQ); readySystemTemplateSearch.and("templateType", readySystemTemplateSearch.entity().getTemplateType(), SearchCriteria.Op.EQ); + readySystemTemplateSearch.and("hypervisorType", readySystemTemplateSearch.entity().getHypervisorType(), SearchCriteria.Op.IN); SearchBuilder templateDownloadSearch = _templateDataStoreDao.createSearchBuilder(); templateDownloadSearch.and("downloadState", templateDownloadSearch.entity().getDownloadState(), SearchCriteria.Op.IN); readySystemTemplateSearch.join("vmTemplateJoinTemplateStoreRef", templateDownloadSearch, templateDownloadSearch.entity().getTemplateId(), readySystemTemplateSearch.entity().getId(), JoinBuilder.JoinType.INNER); - SearchBuilder hostHyperSearch2 = _hostDao.createSearchBuilder(); - hostHyperSearch2.and("type", hostHyperSearch2.entity().getType(), SearchCriteria.Op.EQ); - hostHyperSearch2.and("zoneId", hostHyperSearch2.entity().getDataCenterId(), SearchCriteria.Op.EQ); - hostHyperSearch2.and("removed", hostHyperSearch2.entity().getRemoved(), SearchCriteria.Op.NULL); - hostHyperSearch2.groupBy(hostHyperSearch2.entity().getHypervisorType()); - - readySystemTemplateSearch.join("tmplHyper", hostHyperSearch2, hostHyperSearch2.entity().getHypervisorType(), readySystemTemplateSearch.entity() - .getHypervisorType(), JoinBuilder.JoinType.INNER); - hostHyperSearch2.done(); + readySystemTemplateSearch.groupBy(readySystemTemplateSearch.entity().getId()); readySystemTemplateSearch.done(); tmpltTypeHyperSearch2 = createSearchBuilder(); @@ -397,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); @@ -453,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); @@ -471,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)); } @@ -515,6 +519,48 @@ public List listInZoneByState(long dataCenterId, VirtualMachineTem return listBy(sc); } + @Override + public List listTemplateIsoByArchVnfAndZone(Long dataCenterId, CPU.CPUArch arch, Boolean isIso, + Boolean isVnf) { + GenericSearchBuilder sb = createSearchBuilder(Long.class); + sb.select(null, Func.DISTINCT, sb.entity().getGuestOSId()); + sb.and("state", sb.entity().getState(), SearchCriteria.Op.IN); + sb.and("type", sb.entity().getTemplateType(), SearchCriteria.Op.IN); + sb.and("arch", sb.entity().getArch(), SearchCriteria.Op.EQ); + if (isIso != null) { + sb.and("isIso", sb.entity().getFormat(), isIso ? SearchCriteria.Op.EQ : SearchCriteria.Op.NEQ); + } + if (dataCenterId != null) { + SearchBuilder templateZoneSearch = _templateZoneDao.createSearchBuilder(); + templateZoneSearch.and("removed", templateZoneSearch.entity().getRemoved(), SearchCriteria.Op.NULL); + templateZoneSearch.and("zoneId", templateZoneSearch.entity().getZoneId(), SearchCriteria.Op.EQ); + sb.join("templateZoneSearch", templateZoneSearch, templateZoneSearch.entity().getTemplateId(), + sb.entity().getId(), JoinBuilder.JoinType.INNER); + templateZoneSearch.done(); + } + sb.done(); + SearchCriteria sc = sb.create(); + List types = new ArrayList<>(Arrays.asList(TemplateType.USER, TemplateType.BUILTIN, + TemplateType.PERHOST)); + if (isVnf == null) { + types.add(TemplateType.VNF); + } else if (isVnf) { + types = Collections.singletonList(TemplateType.VNF); + } + sc.setParameters("type", types.toArray()); + sc.setParameters("state", VirtualMachineTemplate.State.Active); + if (dataCenterId != null) { + sc.setJoinParameters("templateZoneSearch", "zoneId", dataCenterId); + } + if (arch != null) { + sc.setParameters("arch", arch); + } + if (isIso != null) { + sc.setParameters("isIso", ImageFormat.ISO); + } + return customSearch(sc, null); + } + @Override public List listAllActive() { SearchCriteria sc = ActiveTmpltSearch.create(); @@ -556,29 +602,96 @@ public VMTemplateVO findSystemVMTemplate(long zoneId) { } @Override - public VMTemplateVO findSystemVMReadyTemplate(long zoneId, HypervisorType hypervisorType) { + public List listAllReadySystemVMTemplates(Long zoneId) { + List availableHypervisors = _hostDao.listDistinctHypervisorTypes(zoneId); + if (CollectionUtils.isEmpty(availableHypervisors)) { + return Collections.emptyList(); + } SearchCriteria sc = readySystemTemplateSearch.create(); sc.setParameters("templateType", Storage.TemplateType.SYSTEM); sc.setParameters("state", VirtualMachineTemplate.State.Active); - sc.setJoinParameters("tmplHyper", "type", Host.Type.Routing); - sc.setJoinParameters("tmplHyper", "zoneId", zoneId); - sc.setJoinParameters("vmTemplateJoinTemplateStoreRef", "downloadState", new VMTemplateStorageResourceAssoc.Status[] {VMTemplateStorageResourceAssoc.Status.DOWNLOADED, VMTemplateStorageResourceAssoc.Status.BYPASSED}); - + sc.setParameters("hypervisorType", availableHypervisors.toArray()); + sc.setJoinParameters("vmTemplateJoinTemplateStoreRef", "downloadState", + List.of(VMTemplateStorageResourceAssoc.Status.DOWNLOADED, + VMTemplateStorageResourceAssoc.Status.BYPASSED).toArray()); // order by descending order of id - List tmplts = listBy(sc, new Filter(VMTemplateVO.class, "id", false, null, null)); + return listBy(sc, new Filter(VMTemplateVO.class, "id", false, null, null)); + } - if (tmplts.size() > 0) { - if (hypervisorType == HypervisorType.Any) { - return tmplts.get(0); - } - for (VMTemplateVO tmplt : tmplts) { - if (tmplt.getHypervisorType() == hypervisorType) { - return tmplt; + @Override + public VMTemplateVO findSystemVMReadyTemplate(long zoneId, HypervisorType hypervisorType, String preferredArch) { + List templates = listAllReadySystemVMTemplates(zoneId); + if (CollectionUtils.isEmpty(templates)) { + return null; + } + if (StringUtils.isNotBlank(preferredArch)) { + // Sort the templates by preferred architecture first + templates = templates.stream() + .sorted(Comparator.comparing( + x -> !x.getArch().getType().equalsIgnoreCase(preferredArch) + )) + .collect(Collectors.toList()); + } + if (hypervisorType == HypervisorType.Any) { + return templates.get(0); + } + return templates.stream() + .filter(t -> t.getHypervisorType() == hypervisorType) + .findFirst() + .orElse(null); + } + + protected List getSortedTemplatesListWithPreferredArch( + Map, VMTemplateVO> uniqueTemplates, String preferredArch) { + List result = new ArrayList<>(uniqueTemplates.values()); + if (StringUtils.isNotBlank(preferredArch)) { + result.sort((t1, t2) -> { + boolean t1Preferred = t1.getArch().getType().equalsIgnoreCase(preferredArch); + boolean t2Preferred = t2.getArch().getType().equalsIgnoreCase(preferredArch); + if (t1Preferred && !t2Preferred) { + return -1; // t1 comes before t2 + } else if (!t1Preferred && t2Preferred) { + return 1; // t2 comes before t1 + } else { + // Both are either preferred or not preferred; use template id as a secondary sorting key. + return Long.compare(t1.getId(), t2.getId()); } - } + }); + } else { + result.sort(Comparator.comparing(VMTemplateVO::getId).reversed()); + } + return result; + } + @Override + public List findSystemVMReadyTemplates(long zoneId, HypervisorType hypervisorType, + String preferredArch) { + List> availableHypervisors = _hostDao.listDistinctHypervisorArchTypes(zoneId); + if (CollectionUtils.isEmpty(availableHypervisors)) { + return Collections.emptyList(); } - return null; + SearchCriteria sc = readySystemTemplateSearch.create(); + sc.setParameters("templateType", Storage.TemplateType.SYSTEM); + sc.setParameters("state", VirtualMachineTemplate.State.Active); + if (hypervisorType != null && !HypervisorType.Any.equals(hypervisorType)) { + sc.setParameters("hypervisorType", List.of(hypervisorType).toArray()); + } else { + sc.setParameters("hypervisorType", + availableHypervisors.stream().map(Pair::first).distinct().toArray()); + } + sc.setJoinParameters("vmTemplateJoinTemplateStoreRef", "downloadState", + List.of(VMTemplateStorageResourceAssoc.Status.DOWNLOADED, + VMTemplateStorageResourceAssoc.Status.BYPASSED).toArray()); + // order by descending order of id + List templates = listBy(sc, new Filter(VMTemplateVO.class, "id", false, null, null)); + Map, VMTemplateVO> uniqueTemplates = new HashMap<>(); + for (VMTemplateVO template : templates) { + Pair key = new Pair<>(template.getHypervisorType(), template.getArch()); + if (availableHypervisors.contains(key) && !uniqueTemplates.containsKey(key)) { + uniqueTemplates.put(key, template); + } + } + return getSortedTemplatesListWithPreferredArch(uniqueTemplates, preferredArch); } @Override @@ -619,10 +732,43 @@ public VMTemplateVO findRoutingTemplate(HypervisorType hType, String templateNam } @Override - public VMTemplateVO findLatestTemplateByTypeAndHypervisor(HypervisorType hypervisorType, TemplateType type) { + public List findRoutingTemplates(HypervisorType hType, String templateName, String preferredArch) { + SearchCriteria sc = tmpltTypeHyperSearch2.create(); + sc.setParameters("templateType", TemplateType.ROUTING); + sc.setParameters("hypervisorType", hType); + sc.setParameters("state", VirtualMachineTemplate.State.Active.toString()); + if (templateName != null) { + sc.setParameters("templateName", templateName); + } + List templates = listBy(sc, new Filter(VMTemplateVO.class, "id", false, null, 1L)); + if (CollectionUtils.isEmpty(templates)) { + sc = tmpltTypeHyperSearch2.create(); + sc.setParameters("templateType", TemplateType.SYSTEM); + sc.setParameters("hypervisorType", hType); + sc.setParameters("state", VirtualMachineTemplate.State.Active.toString()); + if (templateName != null) { + sc.setParameters("templateName", templateName); + } + templates = listBy(sc, new Filter(VMTemplateVO.class, "id", false, null, 1L)); + } + Map, VMTemplateVO> uniqueTemplates = new HashMap<>(); + for (VMTemplateVO template : templates) { + Pair key = new Pair<>(template.getHypervisorType(), template.getArch()); + if (!uniqueTemplates.containsKey(key)) { + uniqueTemplates.put(key, template); + } + } + return getSortedTemplatesListWithPreferredArch(uniqueTemplates, preferredArch); + } + + @Override + public VMTemplateVO findLatestTemplateByTypeAndHypervisorAndArch(HypervisorType hypervisorType, CPU.CPUArch arch, TemplateType type) { SearchCriteria sc = LatestTemplateByHypervisorTypeSearch.create(); sc.setParameters("hypervisorType", hypervisorType); sc.setParameters("templateType", type); + if (arch != null) { + sc.setParameters("arch", arch); + } Filter filter = new Filter(VMTemplateVO.class, "id", false, null, 1L); List templates = listBy(sc, filter); if (templates != null && !templates.isEmpty()) { @@ -686,6 +832,59 @@ public boolean remove(Long id) { return result; } + @Override + public List listIdsByTemplateTag(String tag) { + GenericSearchBuilder sb = createSearchBuilder(Long.class); + sb.selectFields(sb.entity().getId()); + sb.and("tag", sb.entity().getTemplateTag(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("tag", tag); + return customSearchIncludingRemoved(sc, null); + } + + @Override + public List listIdsByExtensionId(long extensionId) { + GenericSearchBuilder sb = createSearchBuilder(Long.class); + sb.selectFields(sb.entity().getId()); + sb.and("extensionId", sb.entity().getExtensionId(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("extensionId", extensionId); + return customSearch(sc, null); + } + + @Override + public 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, @@ -741,7 +940,7 @@ 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; 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 be6588e3189f..a03b94faa797 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 @@ -48,7 +48,7 @@ public interface VolumeDao extends GenericDao, StateDao findIncludingRemovedByInstanceAndType(long id, Volume.Type vType); - List findByInstanceIdAndPoolId(long instanceId, long poolId); + List findNonDestroyedVolumesByInstanceIdAndPoolId(long instanceId, long poolId); List findByInstanceIdDestroyed(long vmId); @@ -70,11 +70,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); @@ -112,7 +112,8 @@ public interface VolumeDao extends GenericDao, StateDao virtualRouters); @@ -155,4 +156,14 @@ public interface VolumeDao extends GenericDao, StateDao listByIds(List ids); + + List listAllocatedVolumesForAccountDiskOfferingIdsAndNotForVms(long accountId, List diskOfferingIds, List vmIds); + + List searchRemovedByVms(List vmIds, Long batchSize); + + VolumeVO findOneByIScsiName(String iScsiName); + + int getVolumeCountByOfferingId(long diskOfferingId); + + VolumeVO findByLastIdAndState(long lastVolumeId, Volume.State...states); } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java index 41c32883d2ef..727c4fe8ef27 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 @@ -23,12 +23,16 @@ import java.util.Collections; import java.util.Date; import java.util.List; +import java.util.stream.Collectors; import javax.inject.Inject; +import org.apache.cloudstack.reservation.ReservationVO; +import org.apache.cloudstack.reservation.dao.ReservationDao; import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; +import com.cloud.configuration.Resource; import com.cloud.exception.InvalidParameterValueException; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.server.ResourceTag.ResourceObjectType; @@ -42,12 +46,16 @@ import com.cloud.tags.dao.ResourceTagDao; import com.cloud.utils.Pair; import com.cloud.utils.db.DB; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.QueryBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Func; import com.cloud.utils.db.SearchCriteria.Op; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallback; import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.db.UpdateBuilder; import com.cloud.utils.exception.CloudRuntimeException; @@ -70,10 +78,13 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol protected GenericSearchBuilder primaryStorageSearch2; protected GenericSearchBuilder secondaryStorageSearch; private final SearchBuilder poolAndPathSearch; + final GenericSearchBuilder CountByOfferingId; + + @Inject + ReservationDao reservationDao; @Inject - ResourceTagDao _tagsDao; + ResourceTagDao tagsDao; - protected static final String SELECT_VM_SQL = "SELECT DISTINCT instance_id from volumes v where v.host_id = ? and v.mirror_state = ?"; // need to account for zone-wide primary storage where storage_pool has // null-value pod and cluster, where hypervisor information is stored in // storage_pool @@ -125,7 +136,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); @@ -134,7 +145,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); @@ -151,7 +162,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); @@ -372,7 +383,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); @@ -387,6 +398,8 @@ public VolumeDaoImpl() { AllFieldsSearch.and("updatedCount", AllFieldsSearch.entity().getUpdatedCount(), Op.EQ); AllFieldsSearch.and("name", AllFieldsSearch.entity().getName(), Op.EQ); AllFieldsSearch.and("passphraseId", AllFieldsSearch.entity().getPassphraseId(), Op.EQ); + AllFieldsSearch.and("iScsiName", AllFieldsSearch.entity().get_iScsiName(), Op.EQ); + AllFieldsSearch.and("path", AllFieldsSearch.entity().getPath(), Op.EQ); AllFieldsSearch.done(); RootDiskStateSearch = createSearchBuilder(); @@ -443,6 +456,7 @@ public VolumeDaoImpl() { CountByAccount.and("account", CountByAccount.entity().getAccountId(), SearchCriteria.Op.EQ); CountByAccount.and("state", CountByAccount.entity().getState(), SearchCriteria.Op.NIN); CountByAccount.and("displayVolume", CountByAccount.entity().isDisplayVolume(), Op.EQ); + CountByAccount.and("idNIN", CountByAccount.entity().getId(), Op.NIN); CountByAccount.done(); primaryStorageSearch = createSearchBuilder(SumCount.class); @@ -454,6 +468,7 @@ public VolumeDaoImpl() { primaryStorageSearch.and("displayVolume", primaryStorageSearch.entity().isDisplayVolume(), Op.EQ); primaryStorageSearch.and("isRemoved", primaryStorageSearch.entity().getRemoved(), Op.NULL); primaryStorageSearch.and("NotCountStates", primaryStorageSearch.entity().getState(), Op.NIN); + primaryStorageSearch.and("idNIN", primaryStorageSearch.entity().getId(), Op.NIN); primaryStorageSearch.done(); primaryStorageSearch2 = createSearchBuilder(SumCount.class); @@ -468,6 +483,7 @@ public VolumeDaoImpl() { primaryStorageSearch2.and("displayVolume", primaryStorageSearch2.entity().isDisplayVolume(), Op.EQ); primaryStorageSearch2.and("isRemoved", primaryStorageSearch2.entity().getRemoved(), Op.NULL); primaryStorageSearch2.and("NotCountStates", primaryStorageSearch2.entity().getState(), Op.NIN); + primaryStorageSearch2.and("idNIN", primaryStorageSearch2.entity().getId(), Op.NIN); primaryStorageSearch2.done(); secondaryStorageSearch = createSearchBuilder(SumCount.class); @@ -492,6 +508,10 @@ public VolumeDaoImpl() { 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(); } @Override @@ -506,15 +526,24 @@ public Pair getCountAndTotalByPool(long poolId) { @Override public Long countAllocatedVolumesForAccount(long accountId) { + List reservations = reservationDao.getReservationsForAccount(accountId, Resource.ResourceType.volume, null); + List reservedResourceIds = reservations.stream().filter(reservation -> reservation.getReservedAmount() > 0).map(ReservationVO::getResourceId).collect(Collectors.toList()); + SearchCriteria sc = CountByAccount.create(); sc.setParameters("account", accountId); - sc.setParameters("state", Volume.State.Destroy, Volume.State.Expunged); + sc.setParameters("state", State.Destroy, State.Expunged); sc.setParameters("displayVolume", 1); + if (CollectionUtils.isNotEmpty(reservedResourceIds)) { + sc.setParameters("idNIN", reservedResourceIds.toArray()); + } return customSearch(sc, null).get(0); } @Override public long primaryStorageUsedForAccount(long accountId, List virtualRouters) { + List reservations = reservationDao.getReservationsForAccount(accountId, Resource.ResourceType.volume, null); + List reservedResourceIds = reservations.stream().filter(reservation -> reservation.getReservedAmount() > 0).map(ReservationVO::getResourceId).collect(Collectors.toList()); + SearchCriteria sc; if (!virtualRouters.isEmpty()) { sc = primaryStorageSearch2.create(); @@ -526,6 +555,9 @@ public long primaryStorageUsedForAccount(long accountId, List virtualRoute sc.setParameters("states", State.Allocated); sc.setParameters("NotCountStates", State.Destroy, State.Expunged); sc.setParameters("displayVolume", 1); + if (CollectionUtils.isNotEmpty(reservedResourceIds)) { + sc.setParameters("idNIN", reservedResourceIds.toArray()); + } List storageSpace = customSearch(sc, null); if (storageSpace != null) { return storageSpace.get(0).sum; @@ -547,27 +579,18 @@ public long secondaryStorageUsedForAccount(long accountId) { } } - public static class SumCount { - public long sum; - public long count; - - public SumCount() { - } - } - @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); } @@ -717,7 +740,7 @@ public boolean remove(Long id) { logger.debug(String.format("Removing volume %s from DB", id)); VolumeVO entry = findById(id); if (entry != null) { - _tagsDao.removeByIdAndType(id, ResourceObjectType.Volume); + tagsDao.removeByIdAndType(id, ResourceObjectType.Volume); } boolean result = super.remove(id); @@ -740,7 +763,7 @@ public boolean updateUuid(long srcVolId, long destVolId) { destVol.setInstanceId(instanceId); update(srcVolId, srcVol); update(destVolId, destVol); - _tagsDao.updateResourceId(srcVolId, destVolId, ResourceObjectType.Volume); + tagsDao.updateResourceId(srcVolId, destVolId, ResourceObjectType.Volume); } catch (Exception e) { throw new CloudRuntimeException("Unable to persist the sequence number for this host"); } @@ -804,6 +827,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()); @@ -839,4 +863,75 @@ public List listByIds(List ids) { sc.setParameters("idIN", ids.toArray()); return listBy(sc, null); } + + @Override + public List listAllocatedVolumesForAccountDiskOfferingIdsAndNotForVms(long accountId, List diskOfferingIds, List vmIds) { + SearchBuilder sb = createSearchBuilder(); + sb.and("account", sb.entity().getAccountId(), SearchCriteria.Op.EQ); + sb.and("state", sb.entity().getState(), SearchCriteria.Op.NIN); + sb.and("diskOfferingIds", sb.entity().getDiskOfferingId(), SearchCriteria.Op.IN); + sb.and("displayVolume", sb.entity().isDisplayVolume(), Op.EQ); + if (CollectionUtils.isNotEmpty(vmIds)) { + sb.and().op("instanceId", sb.entity().getInstanceId(), Op.NULL); + sb.or("notVmIds", sb.entity().getInstanceId(), Op.NIN); + sb.cp(); + } + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("account", accountId); + sc.setParameters("state", Volume.State.Destroy, Volume.State.Expunged); + sc.setParameters("diskOfferingIds", diskOfferingIds.toArray()); + sc.setParameters("displayVolume", 1); + if (CollectionUtils.isNotEmpty(vmIds)) { + sc.setParameters("notVmIds", vmIds.toArray()); + } + return listBy(sc); + } + + @Override + public VolumeVO persist(VolumeVO entity) { + return Transaction.execute((TransactionCallback) status -> { + VolumeVO volume = super.persist(entity); + reservationDao.setResourceId(Resource.ResourceType.volume, volume.getId()); + reservationDao.setResourceId(Resource.ResourceType.primary_storage, volume.getId()); + return volume; + }); + } + + @Override + public List searchRemovedByVms(List vmIds, Long batchSize) { + if (CollectionUtils.isEmpty(vmIds)) { + return new ArrayList<>(); + } + SearchBuilder sb = createSearchBuilder(); + sb.and("vmIds", sb.entity().getInstanceId(), SearchCriteria.Op.IN); + sb.and("removed", sb.entity().getRemoved(), SearchCriteria.Op.NNULL); + SearchCriteria sc = sb.create(); + sc.setParameters("vmIds", vmIds.toArray()); + Filter filter = new Filter(VolumeVO.class, "id", true, 0L, batchSize); + return searchIncludingRemoved(sc, filter, null, false); + } + + @Override + public VolumeVO findOneByIScsiName(String iScsiName) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("iScsiName", iScsiName); + return findOneIncludingRemovedBy(sc); + } + + @Override + public 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); + sc.and(sc.entity().getLastId(), SearchCriteria.Op.EQ, lastVolumeId); + sc.and(sc.entity().getState(), SearchCriteria.Op.IN, (Object[]) states); + return sc.find(); + } } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeStatsDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeStatsDao.java index ff6af56c9c30..b4a596dfc8d5 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeStatsDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeStatsDao.java @@ -75,8 +75,10 @@ public interface VolumeStatsDao extends GenericDao { /** * Removes (expunges) all Volume stats with {@code timestamp} less than * a given Date. - * @param limit the maximum date to keep stored. Records that exceed this limit will be removed. + * @param limitDate the maximum date to keep stored. Records that exceed this limit will be removed. + * @param limitPerQuery the maximum amount of rows to be removed in a single query. We loop if there are still rows to be removed after a given query. + * If 0 or negative, no limit is used. */ - void removeAllByTimestampLessThan(Date limit); + void removeAllByTimestampLessThan(Date limitDate, long limitPerQuery); } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeStatsDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeStatsDaoImpl.java index 5d0d3c8921cf..e4c19fd1666b 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeStatsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeStatsDaoImpl.java @@ -21,6 +21,8 @@ import javax.annotation.PostConstruct; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.stereotype.Component; import com.cloud.utils.db.Filter; @@ -33,6 +35,8 @@ @Component public class VolumeStatsDaoImpl extends GenericDaoBase implements VolumeStatsDao { + protected Logger logger = LogManager.getLogger(getClass()); + protected SearchBuilder volumeIdSearch; protected SearchBuilder volumeIdTimestampGreaterThanEqualSearch; protected SearchBuilder volumeIdTimestampLessThanEqualSearch; @@ -116,9 +120,14 @@ public void removeAllByVolumeId(long volumeId) { } @Override - public void removeAllByTimestampLessThan(Date limit) { + public void removeAllByTimestampLessThan(Date limitDate, long limitPerQuery) { SearchCriteria sc = timestampSearch.create(); - sc.setParameters(TIMESTAMP, limit); - expunge(sc); + sc.setParameters(TIMESTAMP, limitDate); + + logger.debug(String.format("Starting to remove all volume_stats rows older than [%s].", limitDate)); + + long totalRemoved = batchExpunge(sc, limitPerQuery); + + logger.info(String.format("Removed a total of [%s] volume_stats rows older than [%s].", totalRemoved, limitDate)); } } diff --git a/engine/schema/src/main/java/com/cloud/upgrade/ConfigurationGroupsAggregator.java b/engine/schema/src/main/java/com/cloud/upgrade/ConfigurationGroupsAggregator.java index 03857137ded6..5c1a75046927 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/ConfigurationGroupsAggregator.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/ConfigurationGroupsAggregator.java @@ -54,7 +54,7 @@ public ConfigurationGroupsAggregator() { public void updateConfigurationGroups() { LOG.debug("Updating configuration groups"); - List configs = configDao.listAllIncludingRemoved(); + List configs = configDao.searchPartialConfigurations(); if (CollectionUtils.isEmpty(configs)) { return; } 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 7bf5cf627902..cccfbe8a0065 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseCreator.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseCreator.java @@ -74,7 +74,7 @@ private static void runScript(Connection conn, Reader reader, String filename, b private static void runQuery(String host, String port, String rootPassword, String query, boolean dryRun) { System.out.println("============> Running query: " + query); - try (Connection conn = DriverManager.getConnection(String.format("jdbc:mysql://%s:%s/", host, port), "root", rootPassword); + try (Connection conn = DriverManager.getConnection(String.format("jdbc:mysql://%s:%s/?" + TransactionLegacy.CONNECTION_PARAMS, host, port), "root", rootPassword); Statement stmt = conn.createStatement();){ if (!dryRun) stmt.executeUpdate(query); @@ -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)); @@ -116,10 +116,6 @@ private static void initDB(String dbPropsFile, String rootPassword, String[] dat } public static void main(String[] args) { - - ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] {"/com/cloud/upgrade/databaseCreatorContext.xml"}); - appContext.getBean(ComponentContext.class); - String dbPropsFile = ""; List sqlFiles = new ArrayList(); List upgradeClasses = new ArrayList(); @@ -166,13 +162,17 @@ public static void main(String[] args) { System.exit(1); } + initDB(dbPropsFile, rootPassword, databases, dryRun); + + ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] {"/com/cloud/upgrade/databaseCreatorContext.xml"}); + appContext.getBean(ComponentContext.class); + try { TransactionLegacy.initDataSource(dbPropsFile); } catch (IOException e) { e.printStackTrace(); System.exit(1); } - initDB(dbPropsFile, rootPassword, databases, dryRun); // Process sql files for (String sqlFile : sqlFiles) { 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 ea8ce47611a2..0e784d961b3d 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java @@ -33,12 +33,10 @@ import javax.inject.Inject; -import com.cloud.upgrade.dao.Upgrade41900to42000; -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; @@ -87,8 +85,15 @@ import com.cloud.upgrade.dao.Upgrade41720to41800; import com.cloud.upgrade.dao.Upgrade41800to41810; import com.cloud.upgrade.dao.Upgrade41810to41900; +import com.cloud.upgrade.dao.Upgrade41900to41910; +import com.cloud.upgrade.dao.Upgrade41910to42000; +import com.cloud.upgrade.dao.Upgrade42000to42010; +import com.cloud.upgrade.dao.Upgrade42010to42100; +import com.cloud.upgrade.dao.Upgrade42100to42200; +import com.cloud.upgrade.dao.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; @@ -117,18 +122,24 @@ 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 { protected static Logger LOGGER = LogManager.getLogger(DatabaseUpgradeChecker.class); private final DatabaseVersionHierarchy hierarchy; private static final String VIEWS_DIRECTORY = Paths.get("META-INF", "db", "views").toString(); + private static final String PROCEDURES_DIRECTORY = Paths.get("META-INF", "db", "procedures").toString(); @Inject VersionDao _dao; @@ -226,7 +237,13 @@ public DatabaseUpgradeChecker() { .next("4.17.2.0", new Upgrade41720to41800()) .next("4.18.0.0", new Upgrade41800to41810()) .next("4.18.1.0", new Upgrade41810to41900()) - .next("4.19.0.0", new Upgrade41900to42000()) + .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.21.0.0", new Upgrade42100to42200()) + .next("4.22.0.0", new Upgrade42200to42210()) + .next("4.22.1.0", new Upgrade42210to42300()) .build(); } @@ -242,7 +259,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 @@ -296,100 +312,140 @@ private void updateSystemVmTemplates(DbUpgrade[] upgrades) { } protected void upgrade(CloudStackVersion dbVersion, CloudStackVersion currentVersion) { - LOGGER.info("Database upgrade must be performed from " + dbVersion + " to " + currentVersion); + executeProcedureScripts(); + final DbUpgrade[] upgrades = executeUpgrades(dbVersion, currentVersion); - final DbUpgrade[] upgrades = calculateUpgradePath(dbVersion, currentVersion); + executeViewScripts(); + updateSystemVmTemplates(upgrades); + } - for (DbUpgrade upgrade : upgrades) { - VersionVO version; - LOGGER.debug("Running upgrade " + upgrade.getClass().getSimpleName() + " to upgrade from " + upgrade.getUpgradableVersionRange()[0] + "-" + upgrade - .getUpgradableVersionRange()[1] + " to " + upgrade.getUpgradedVersion()); - TransactionLegacy txn = TransactionLegacy.open("Upgrade"); - txn.start(); - try { - Connection conn; - try { - conn = txn.getConnection(); - } catch (SQLException e) { - String errorMessage = "Unable to upgrade the database"; - LOGGER.error(errorMessage, e); - throw new CloudRuntimeException(errorMessage, e); - } - InputStream[] scripts = upgrade.getPrepareScripts(); - if (scripts != null) { - for (InputStream script : scripts) { - runScript(conn, script); - } - } + protected void executeProcedureScripts() { + LOGGER.info("Executing Stored Procedure scripts that are under resource directory [{}].", PROCEDURES_DIRECTORY); + List filesPathUnderViewsDirectory = FileUtil.getFilesPathsUnderResourceDirectory(PROCEDURES_DIRECTORY); - upgrade.performDataMigration(conn); + try (TransactionLegacy txn = TransactionLegacy.open("execute-procedure-scripts")) { + Connection conn = txn.getConnection(); - version = new VersionVO(upgrade.getUpgradedVersion()); - version = _dao.persist(version); + for (String filePath : filesPathUnderViewsDirectory) { + LOGGER.debug("Executing PROCEDURE script [{}].", filePath); - txn.commit(); - } catch (CloudRuntimeException e) { + InputStream viewScript = Thread.currentThread().getContextClassLoader().getResourceAsStream(filePath); + runScript(conn, viewScript); + } + + 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); + throw new CloudRuntimeException(message, e); + } + } + + private DbUpgrade[] executeUpgrades(CloudStackVersion dbVersion, CloudStackVersion currentVersion) { + LOGGER.info("Database upgrade must be performed from {} to {}", dbVersion, currentVersion); + + final DbUpgrade[] upgrades = calculateUpgradePath(dbVersion, currentVersion); + + for (DbUpgrade upgrade : upgrades) { + VersionVO version = executeUpgrade(upgrade); + executeUpgradeCleanup(upgrade, version); + } + return upgrades; + } + + private VersionVO executeUpgrade(DbUpgrade upgrade) { + VersionVO version; + 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 { + Connection conn; + try { + conn = txn.getConnection(); + } catch (SQLException e) { String errorMessage = "Unable to upgrade the database"; LOGGER.error(errorMessage, e); throw new CloudRuntimeException(errorMessage, e); - } finally { - txn.close(); + } + InputStream[] scripts = upgrade.getPrepareScripts(); + if (scripts != null) { + for (InputStream script : scripts) { + runScript(conn, script); + } } - // 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()); + upgrade.performDataMigration(conn); - txn.start(); - Connection conn; - try { - conn = txn.getConnection(); - } catch (SQLException e) { - LOGGER.error("Unable to cleanup the database", e); - throw new CloudRuntimeException("Unable to cleanup the database", e); - } + version = new VersionVO(upgrade.getUpgradedVersion()); + version = _dao.persist(version); - InputStream[] scripts = upgrade.getCleanupScripts(); - if (scripts != null) { - for (InputStream script : scripts) { - runScript(conn, script); - LOGGER.debug("Cleanup script " + upgrade.getClass().getSimpleName() + " is executed successfully"); - } - } - txn.commit(); + txn.commit(); + } catch (CloudRuntimeException e) { + String errorMessage = "Unable to upgrade the database"; + LOGGER.error(errorMessage, e); + throw new CloudRuntimeException(errorMessage, e); + } finally { + txn.close(); + } + if (upgrade.refreshPoolConnectionsAfterUpgrade()) { + TransactionLegacy.refreshConnections(TransactionLegacy.CLOUD_DB); + } + return version; + } - txn.start(); - version.setStep(Step.Complete); - version.setUpdated(new Date()); - _dao.update(version.getId(), version); - txn.commit(); - LOGGER.debug("Upgrade completed for version " + version.getVersion()); - } finally { - txn.close(); + private void executeUpgradeCleanup(DbUpgrade upgrade, VersionVO version) { + TransactionLegacy txn; + // Run the corresponding '-cleanup.sql' script + txn = TransactionLegacy.open("Cleanup"); + try { + LOGGER.info("Cleanup upgrade {} to upgrade from {}-{} to {}", upgrade.getClass().getSimpleName(), upgrade.getUpgradableVersionRange()[0], upgrade + .getUpgradableVersionRange()[1], upgrade.getUpgradedVersion()); + + txn.start(); + Connection conn; + try { + conn = txn.getConnection(); + } catch (SQLException e) { + LOGGER.error("Unable to cleanup the database", e); + throw new CloudRuntimeException("Unable to cleanup the database", e); } - } - executeViewScripts(); - updateSystemVmTemplates(upgrades); + InputStream[] scripts = upgrade.getCleanupScripts(); + if (scripts != null) { + for (InputStream script : scripts) { + runScript(conn, script); + LOGGER.debug("Cleanup script {} is executed successfully", upgrade.getClass().getSimpleName()); + } + } + txn.commit(); + + txn.start(); + version.setStep(Step.Complete); + version.setUpdated(new Date()); + _dao.update(version.getId(), version); + txn.commit(); + 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); @@ -406,43 +462,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; + } + }); + } + + @VisibleForTesting + protected void doUpgrades(GlobalLock lock) { + try { + initializeDatabaseEncryptors(); - 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()); + final CloudStackVersion dbVersion = CloudStackVersion.parse(_dao.getCurrentVersion()); + final String currentVersionValue = getImplementationVersion(); - LOGGER.info("DB version = " + dbVersion + " Code Version = " + currentVersion); + if (StringUtils.isBlank(currentVersionValue)) { + return; + } - if (dbVersion.compareTo(currentVersion) > 0) { - throw new CloudRuntimeException("Database version " + dbVersion + " is higher than management software version " + currentVersionValue); - } + 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()); - if (dbVersion.compareTo(currentVersion) == 0) { - LOGGER.info("DB version and code version matches so no upgrade needed."); - return; - } + 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"; @@ -471,7 +585,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)); } } } @@ -502,21 +616,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/SystemVmTemplateRegistration.java b/engine/schema/src/main/java/com/cloud/upgrade/SystemVmTemplateRegistration.java index 428bd0260b87..292bafefbb65 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/SystemVmTemplateRegistration.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/SystemVmTemplateRegistration.java @@ -16,18 +16,64 @@ // under the License. package com.cloud.upgrade; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.sql.Connection; +import java.sql.Date; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + +import javax.inject.Inject; + +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.config.dao.ConfigurationDaoImpl; +import org.apache.cloudstack.framework.config.impl.ConfigurationVO; +import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; +import org.apache.cloudstack.storage.datastore.db.ImageStoreDaoImpl; +import org.apache.cloudstack.storage.datastore.db.ImageStoreDetailsDao; +import org.apache.cloudstack.storage.datastore.db.ImageStoreDetailsDaoImpl; +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.cloudstack.utils.server.ServerPropertiesUtil; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.ini4j.Ini; + +import com.cloud.cpu.CPU; import com.cloud.dc.DataCenterVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.ClusterDaoImpl; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.DataCenterDaoImpl; +import com.cloud.dc.dao.DataCenterDetailsDao; +import com.cloud.dc.dao.DataCenterDetailsDaoImpl; import com.cloud.hypervisor.Hypervisor; import com.cloud.storage.DataStoreRole; +import com.cloud.storage.GuestOSVO; import com.cloud.storage.Storage; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.VMTemplateStorageResourceAssoc; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.VMTemplateZoneVO; +import com.cloud.storage.dao.GuestOSDao; +import com.cloud.storage.dao.GuestOSDaoImpl; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VMTemplateDaoImpl; import com.cloud.storage.dao.VMTemplateZoneDao; @@ -36,6 +82,7 @@ import com.cloud.upgrade.dao.BasicTemplateDataStoreDaoImpl; import com.cloud.user.Account; import com.cloud.utils.DateUtil; +import com.cloud.utils.HttpUtils; import com.cloud.utils.Pair; import com.cloud.utils.UriUtils; import com.cloud.utils.db.GlobalLock; @@ -46,45 +93,10 @@ import com.cloud.utils.script.Script; import com.cloud.vm.dao.VMInstanceDao; import com.cloud.vm.dao.VMInstanceDaoImpl; -import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; -import org.apache.cloudstack.framework.config.dao.ConfigurationDao; -import org.apache.cloudstack.framework.config.dao.ConfigurationDaoImpl; -import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; -import org.apache.cloudstack.storage.datastore.db.ImageStoreDaoImpl; -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.apache.logging.log4j.Logger; -import org.apache.logging.log4j.LogManager; -import org.ini4j.Ini; - -import javax.inject.Inject; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.sql.Connection; -import java.sql.Date; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.stream.Collectors; public class SystemVmTemplateRegistration { protected static Logger LOGGER = LogManager.getLogger(SystemVmTemplateRegistration.class); - private static final String MOUNT_COMMAND = "sudo mount -t nfs %s %s"; + private static final String MOUNT_COMMAND_BASE = "sudo mount -t nfs"; private static final String UMOUNT_COMMAND = "sudo umount %s"; private static final String RELATIVE_TEMPLATE_PATH = "./engine/schema/dist/systemvm-templates/"; private static final String ABSOLUTE_TEMPLATE_PATH = "/usr/share/cloudstack-management/templates/systemvm/"; @@ -93,13 +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 final Integer LINUX_5_ID = 15; - private static final Integer LINUX_7_ID = 183; + 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; @@ -117,39 +136,44 @@ public class SystemVmTemplateRegistration { @Inject ImageStoreDao imageStoreDao; @Inject + ImageStoreDetailsDao imageStoreDetailsDao; + @Inject ClusterDao clusterDao; @Inject ConfigurationDao configurationDao; + @Inject + DataCenterDetailsDao dataCenterDetailsDao; + @Inject + GuestOSDao guestOSDao; private String systemVmTemplateVersion; + private final File tempDownloadDir; + public SystemVmTemplateRegistration() { dataCenterDao = new DataCenterDaoImpl(); + dataCenterDetailsDao = new DataCenterDetailsDaoImpl(); vmTemplateDao = new VMTemplateDaoImpl(); vmTemplateZoneDao = new VMTemplateZoneDaoImpl(); templateDataStoreDao = new BasicTemplateDataStoreDaoImpl(); vmInstanceDao = new VMInstanceDaoImpl(); imageStoreDao = new ImageStoreDaoImpl(); + imageStoreDetailsDao = new ImageStoreDetailsDaoImpl(); clusterDao = new ClusterDaoImpl(); configurationDao = new ConfigurationDaoImpl(); + guestOSDao = new GuestOSDaoImpl(); + tempDownloadDir = new File(System.getProperty("java.io.tmpdir")); } /** - * 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 String getSystemVmTemplateVersion() { - if (StringUtils.isEmpty(systemVmTemplateVersion)) { - return String.format("%s.%s", CS_MAJOR_VERSION, CS_TINY_VERSION); - } - return systemVmTemplateVersion; - } - - private static class SystemVMTemplateDetails { + protected static class SystemVMTemplateDetails { Long id; String uuid; String name; @@ -160,6 +184,7 @@ private static class SystemVMTemplateDetails { ImageFormat format; Integer guestOsId; Hypervisor.HypervisorType hypervisorType; + CPU.CPUArch arch; Long storeId; Long size; Long physicalSize; @@ -169,7 +194,7 @@ private static class SystemVMTemplateDetails { SystemVMTemplateDetails(String uuid, String name, Date created, String url, String checksum, ImageFormat format, Integer guestOsId, Hypervisor.HypervisorType hypervisorType, - Long storeId) { + CPU.CPUArch arch, Long storeId) { this.uuid = uuid; this.name = name; this.created = created; @@ -178,6 +203,7 @@ private static class SystemVMTemplateDetails { this.format = format; this.guestOsId = guestOsId; this.hypervisorType = hypervisorType; + this.arch = arch; this.storeId = storeId; } @@ -221,6 +247,10 @@ public Hypervisor.HypervisorType getHypervisorType() { return hypervisorType; } + public CPU.CPUArch getArch() { + return arch; + } + public Long getStoreId() { return storeId; } @@ -274,20 +304,19 @@ public void setUpdated(Date updated) { } } - public static final List hypervisorList = Arrays.asList(Hypervisor.HypervisorType.KVM, - Hypervisor.HypervisorType.VMware, - Hypervisor.HypervisorType.XenServer, - Hypervisor.HypervisorType.Hyperv, - Hypervisor.HypervisorType.LXC, - Hypervisor.HypervisorType.Ovm3 + 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, 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 NewTemplateNameList = new HashMap(); - public static final Map FileNames = new HashMap(); - public static final Map NewTemplateUrl = new HashMap(); - public static final Map NewTemplateChecksum = 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"); @@ -298,18 +327,7 @@ public void setUpdated(Date updated) { } }; - public static final Map hypervisorGuestOsMap = new HashMap() { - { - put(Hypervisor.HypervisorType.KVM, LINUX_5_ID); - put(Hypervisor.HypervisorType.XenServer, OTHER_LINUX_ID); - put(Hypervisor.HypervisorType.VMware, OTHER_LINUX_ID); - put(Hypervisor.HypervisorType.Hyperv, LINUX_5_ID); - put(Hypervisor.HypervisorType.LXC, LINUX_5_ID); - put(Hypervisor.HypervisorType.Ovm3, LINUX_7_ID); - } - }; - - 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); @@ -320,89 +338,231 @@ public void setUpdated(Date updated) { } }; - public static boolean validateIfSeeded(String url, String path) { - 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); - 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()) { - 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()); + sb.append(", arch: ").append(arch == null ? CPU.CPUArch.amd64.getType() : arch.getType()); + return sb.toString(); + } + + /** + * Attempts to determine the templates directory path by locating the metadata file. + *

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

+ *

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

+ *

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

+ * + * @return the path to the templates directory if the metadata file is found, or {@code RELATIVE_TEMPLATE_PATH} + * when running in a test environment. + * @throws CloudRuntimeException if the metadata file cannot be located in any of the candidate paths. + */ + private static String fetchTemplatesPath() { + if (isRunningInTest()) { + return RELATIVE_TEMPLATE_PATH; + } + List paths = Arrays.asList(RELATIVE_TEMPLATE_PATH, ABSOLUTE_TEMPLATE_PATH); + for (String path : paths) { + String filePath = path + METADATA_FILE_NAME; + LOGGER.debug("Looking for file [ {} ] in the classpath.", filePath); + File metaFile = new File(filePath); + if (metaFile.exists()) { + return path; } } + String errMsg = String.format("Unable to locate metadata file in your setup at %s", StringUtils.join(paths)); + LOGGER.error(errMsg); + throw new CloudRuntimeException(errMsg); } - public Long getRegisteredTemplateId(Pair hypervisorAndTemplateName) { - VMTemplateVO vmTemplate = vmTemplateDao.findLatestTemplateByName(hypervisorAndTemplateName.second()); - Long templateId = null; - if (vmTemplate != null) { - templateId = vmTemplate.getId(); + 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); } - return templateId; } - private static String fetchTemplatesPath() { - String filePath = RELATIVE_TEMPLATE_PATH + METADATA_FILE_NAME; - LOGGER.debug(String.format("Looking for file [ %s ] in the classpath.", filePath)); - File metaFile = new File(filePath); - String templatePath = null; - if (metaFile.exists()) { - templatePath = RELATIVE_TEMPLATE_PATH; - } - if (templatePath == null) { - filePath = ABSOLUTE_TEMPLATE_PATH + METADATA_FILE_NAME; - metaFile = new File(filePath); - templatePath = ABSOLUTE_TEMPLATE_PATH; - LOGGER.debug(String.format("Looking for file [ %s ] in the classpath.", filePath)); - if (!metaFile.exists()) { - String errMsg = String.format("Unable to locate metadata file in your setup at %s", filePath.toString()); - LOGGER.error(errMsg); - throw new CloudRuntimeException(errMsg); + 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 templatePath; + return new Pair<>(size, physicalSize); + } + + 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); } - private String getHypervisorName(String name) { - if (name.equals("xenserver")) { - return "xen"; + 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); } - if (name.equals("ovm3")) { - return "ovm"; + 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 name; + 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); + } + 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) { + try { + if (storeUrl == null) { + return; + } + URI uri = new URI(UriUtils.encodeURIComponent(storeUrl)); + String host = uri.getHost(); + String mountPath = uri.getPath(); + Script.runSimpleBashScript(getMountCommand(nfsVersion, host + ":" + mountPath, path)); + } catch (Exception e) { + String msg = "NFS Store URL is not in the correct format"; + LOGGER.error(msg, e); + throw new CloudRuntimeException(msg, e); + } } - private Hypervisor.HypervisorType getHypervisorType(String hypervisor) { - if (hypervisor.equalsIgnoreCase("xen")) { - hypervisor = "xenserver"; - } else if (hypervisor.equalsIgnoreCase("ovm")) { - hypervisor = "ovm3"; + 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); } - return Hypervisor.HypervisorType.getType(hypervisor); } - private List getEligibleZoneIds() { + 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) { @@ -413,41 +573,24 @@ private List getEligibleZoneIds() { return zoneIds; } - private Pair getNfsStoreInZone(Long zoneId) { - String url = null; - Long storeId = null; + protected Pair getNfsStoreInZone(Long zoneId) { ImageStoreVO storeVO = imageStoreDao.findOneByZoneAndProtocol(zoneId, "nfs"); if (storeVO == null) { - String errMsg = String.format("Failed to fetch NFS store in zone = %s for SystemVM template registration", zoneId); + String errMsg = String.format("Failed to fetch NFS store in zone = %s for SystemVM Template registration", + zoneId); LOGGER.error(errMsg); throw new CloudRuntimeException(errMsg); } - url = storeVO.getUrl(); - storeId = storeVO.getId(); + String url = storeVO.getUrl(); + Long storeId = storeVO.getId(); return new Pair<>(url, storeId); } - public static void mountStore(String storeUrl, String path) { - try { - if (storeUrl != null) { - URI uri = new URI(UriUtils.encodeURIComponent(storeUrl)); - String host = uri.getHost(); - String mountPath = uri.getPath(); - String mount = String.format(MOUNT_COMMAND, host + ":" + mountPath, path); - Script.runSimpleBashScript(mount); - } - } catch (Exception e) { - String msg = "NFS Store URL is not in the correct format"; - LOGGER.error(msg, e); - throw new CloudRuntimeException(msg, e); + protected String getSystemVmTemplateVersion() { + if (StringUtils.isEmpty(systemVmTemplateVersion)) { + return String.format("%s.%s", CS_MAJOR_VERSION, CS_TINY_VERSION); } - } - - private List fetchAllHypervisors(Long zoneId) { - List hypervisorList = new ArrayList<>(); - List hypervisorTypes = clusterDao.getAvailableHypervisorInZone(zoneId); - hypervisorList = hypervisorTypes.stream().distinct().map(Enum::name).collect(Collectors.toList()); - return hypervisorList; + return systemVmTemplateVersion; } private VMTemplateVO createTemplateObjectInDB(SystemVMTemplateDetails details) { @@ -463,60 +606,65 @@ 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()); template.setGuestOSId(details.getGuestOsId()); template.setCrossZones(true); template.setHypervisorType(details.getHypervisorType()); + template.setArch(details.getArch()); template.setState(VirtualMachineTemplate.State.Inactive); template.setDeployAsIs(false); template = vmTemplateDao.persist(template); 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()); templateZoneVO = vmTemplateZoneDao.persist(templateZoneVO); } else { templateZoneVO.setLastUpdated(new java.util.Date()); - if (vmTemplateZoneDao.update(templateZoneVO.getId(), templateZoneVO)) { + if (!vmTemplateZoneDao.update(templateZoneVO.getId(), templateZoneVO)) { templateZoneVO = null; } } return templateZoneVO; } - private void createCrossZonesTemplateZoneRefEntries(VMTemplateVO template) { + protected void createCrossZonesTemplateZoneRefEntries(Long templateId) { List dcs = dataCenterDao.listAll(); for (DataCenterVO dc : dcs) { - VMTemplateZoneVO templateZoneVO = createOrUpdateTemplateZoneEntry(dc.getId(), template.getId()); + 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 for hypervisor: %s and zone: %s", template.getHypervisorType().name(), 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); @@ -525,157 +673,199 @@ 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 updateSystemVMEntries(Long templateId, Hypervisor.HypervisorType hypervisorType) { + protected void updateSeededTemplateDetails(long templateId, long storeId, long size, long physicalSize) { + VMTemplateVO template = vmTemplateDao.findById(templateId); + template.setSize(size); + vmTemplateDao.update(template.getId(), template); + + TemplateDataStoreVO templateDataStoreVO = templateDataStoreDao.findByStoreTemplate(storeId, template.getId()); + templateDataStoreVO.setSize(size); + templateDataStoreVO.setPhysicalSize(physicalSize); + templateDataStoreVO.setLastUpdated(new Date(DateUtil.currentGMTTime().getTime())); + boolean updated = templateDataStoreDao.update(templateDataStoreVO.getId(), templateDataStoreVO); + if (!updated) { + throw new CloudRuntimeException("Failed to update template-store record for seeded system VM Template"); + } + } + + protected void updateSystemVMEntries(Long templateId, Hypervisor.HypervisorType hypervisorType) { vmInstanceDao.updateSystemVmTemplateId(templateId, hypervisorType); } - 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 updateHypervisorGuestOsMap() { + try { + 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 System VM template guest OS ID, due to {}", e.getMessage()); } } - private static void readTemplateProperties(String path, SystemVMTemplateDetails details) { - 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); + 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)); + } + 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); } - details.setSize(size); - details.setPhysicalSize(physicalSize); } - 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, Pair hypervisorAndTemplateName, - 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); - scr.add("-f", TEMPLATES_PATH + FileNames.get(hypervisorAndTemplateName.first())); - scr.add("-h", hypervisorAndTemplateName.first().name().toLowerCase(Locale.ROOT)); + String filePath = StringUtils.isNotBlank(templateDetails.getDownloadedFilePath()) ? + templateDetails.getDownloadedFilePath() : + templateDetails.getDefaultFilePath(); + scr.add("-f", filePath); + 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(Pair hypervisorAndTemplateName, - String url, String checksum, ImageFormat format, long guestOsId, - Long storeId, Long templateId, String filePath, TemplateDataStoreVO templateDataStoreVO) { - Hypervisor.HypervisorType hypervisor = hypervisorAndTemplateName.first(); + /** + * 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, hypervisorAndTemplateName.second(), created, - url, checksum, format, (int) guestOsId, hypervisor, 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(); - createCrossZonesTemplateZoneRefEntries(template); } + createCrossZonesTemplateZoneRefEntries(templateId); + 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, hypervisorAndTemplateName, destTempFolder); + setupTemplateOnStore(templateName, templateDetails, destTempFolder); readTemplateProperties(destTempFolder + "/template.properties", details); details.setUpdated(new Date(DateUtil.currentGMTTime().getTime())); updateTemplateDetails(details); return templateId; } - public void registerTemplate(Pair hypervisorAndTemplateName, - Pair storeUrlAndId, VMTemplateVO templateVO, - TemplateDataStoreVO templateDataStoreVO, String filePath) { - Long templateId = null; + /** + * 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 { - templateId = templateVO.getId(); - performTemplateRegistrationOperations(hypervisorAndTemplateName, templateVO.getUrl(), templateVO.getChecksum(), - templateVO.getFormat(), templateVO.getGuestOSId(), storeUrlAndId.second(), templateId, filePath, templateDataStoreVO); + 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", hypervisorAndTemplateName.first()); + String errMsg = String.format("Failed to add %s to store ID: %d, zone ID: %d", templateVO, storeId, zoneId); LOGGER.error(errMsg, e); - if (templateId != null) { - updateTemplateTablesOnFailure(templateId); - cleanupStore(templateId, filePath); - } + cleanupStore(templateVO.getId(), filePath); throw new CloudRuntimeException(errMsg, e); } } - public void registerTemplate(Pair hypervisorAndTemplateName, 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 { - Hypervisor.HypervisorType hypervisor = hypervisorAndTemplateName.first(); - templateId = performTemplateRegistrationOperations(hypervisorAndTemplateName, NewTemplateUrl.get(hypervisor), NewTemplateChecksum.get(hypervisor), - hypervisorImageFormat.get(hypervisor), hypervisorGuestOsMap.get(hypervisor), storeUrlAndId.second(), null, filePath, null); - Map configParams = new HashMap<>(); - configParams.put(RouterTemplateConfigurationNames.get(hypervisorAndTemplateName.first()), hypervisorAndTemplateName.second()); - configParams.put("minreq.sysvmtemplate.version", getSystemVmTemplateVersion()); - updateConfigurationParams(configParams); - updateSystemVMEntries(templateId, hypervisorAndTemplateName.first()); + 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", hypervisorAndTemplateName.first()); + 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); @@ -683,82 +873,149 @@ public void registerTemplate(Pair hypervisorA } /** - * 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. + * 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 */ - public static String parseMetadataFile() { - try { - Ini ini = new Ini(); - ini.load(new FileReader(METADATA_FILE)); - for (Hypervisor.HypervisorType hypervisorType : hypervisorList) { - String hypervisor = hypervisorType.name().toLowerCase(Locale.ROOT); - Ini.Section section = ini.get(hypervisor); - NewTemplateNameList.put(hypervisorType, section.get("templatename")); - FileNames.put(hypervisorType, section.get("filename")); - NewTemplateChecksum.put(hypervisorType, section.get("checksum")); - NewTemplateUrl.put(hypervisorType, section.get("downloadurl")); - } - Ini.Section section = ini.get("default"); - return section.get("version"); - } catch (Exception e) { - String errMsg = String.format("Failed to parse systemVM template metadata file: %s", METADATA_FILE); - LOGGER.error(errMsg, e); - throw new CloudRuntimeException(errMsg, e); + 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"); } - } - - 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); + 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 (templateDetails.isFileChecksumDifferent(templateFile)) { + throw new CloudRuntimeException("Checksum failed for local template file"); } + return templateDetails; } - private void validateTemplates(Set hypervisorsInUse) { - Set hypervisors = hypervisorsInUse.stream().map(Enum::name). - map(name -> name.toLowerCase(Locale.ROOT)).map(this::getHypervisorName).collect(Collectors.toSet()); - List templates = new ArrayList<>(); - for (Hypervisor.HypervisorType hypervisorType : hypervisorsInUse) { - templates.add(FileNames.get(hypervisorType)); + /** + * 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 + */ + protected File getTemplateFile(MetadataTemplateDetails templateDetails) { + File templateFile = new File(templateDetails.getDefaultFilePath()); + if (templateFile.exists()) { + return templateFile; + } + LOGGER.debug("{} is not present", templateFile.getAbsolutePath()); + 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(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()); } + return templateFile; + } + /** + * 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 (String hypervisor : hypervisors) { - String matchedTemplate = templates.stream().filter(x -> x.contains(hypervisor)).findAny().orElse(null); - if (matchedTemplate == null) { - templatesFound = false; - break; + 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; } - - File tempFile = new File(TEMPLATES_PATH + matchedTemplate); - String templateChecksum = DigestHelper.calculateChecksum(tempFile); - if (!templateChecksum.equals(NewTemplateChecksum.get(getHypervisorType(hypervisor)))) { - LOGGER.error(String.format("Checksum mismatch: %s != %s ", templateChecksum, NewTemplateChecksum.get(getHypervisorType(hypervisor)))); + 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); } } - public void registerTemplates(Set hypervisorsInUse) { + /** + * 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(), storeMountPath, nfsVersion); + List> hypervisorArchList = + clusterDao.listDistinctHypervisorsAndArchExcludingExternalType(zoneId); + for (Pair hypervisorArch : hypervisorArchList) { + Hypervisor.HypervisorType hypervisorType = hypervisorArch.first(); + MetadataTemplateDetails templateDetails = getMetadataTemplateDetails(hypervisorType, + hypervisorArch.second()); + if (templateDetails == null) { + continue; + } + VMTemplateVO templateVO = getRegisteredTemplate(templateDetails.getName(), + templateDetails.getHypervisorType(), templateDetails.getArch(), templateDetails.getUrl()); + if (templateVO != null) { + TemplateDataStoreVO templateDataStoreVO = + templateDataStoreDao.findByStoreTemplate(storeUrlAndId.second(), templateVO.getId()); + if (templateDataStoreVO != null) { + String installPath = templateDataStoreVO.getInstallPath(); + if (validateIfSeeded(templateDataStoreVO, storeUrlAndId.first(), installPath, nfsVersion)) { + continue; + } + } + addExistingTemplateToStore(templateVO, templateDetails, templateDataStoreVO, zoneId, + storeUrlAndId.second(), storeMountPath); + updateRegisteredTemplateDetails(templateVO.getId(), templateDetails, zoneId); + continue; + } + registerNewTemplate(templateDetails.getName(), templateDetails, zoneId, storeUrlAndId.second(), + storeMountPath); + } + } + + 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(hypervisorsInUse); + validateTemplates(hypervisorsArchInUse); // Perform Registration if templates not already registered Transaction.execute(new TransactionCallbackNoReturn() { @Override @@ -771,37 +1028,17 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { if (filePath == null) { throw new CloudRuntimeException("Failed to create temporary file path to mount the store"); } - Pair storeUrlAndId = getNfsStoreInZone(zoneId); - mountStore(storeUrlAndId.first(), filePath); - List hypervisorList = fetchAllHypervisors(zoneId); - for (String hypervisor : hypervisorList) { - Hypervisor.HypervisorType name = Hypervisor.HypervisorType.getType(hypervisor); - String templateName = NewTemplateNameList.get(name); - Pair hypervisorAndTemplateName = new Pair(name, templateName); - Long templateId = getRegisteredTemplateId(hypervisorAndTemplateName); - if (templateId != null) { - VMTemplateVO templateVO = vmTemplateDao.findById(templateId); - TemplateDataStoreVO templateDataStoreVO = templateDataStoreDao.findByTemplate(templateId, DataStoreRole.Image); - String installPath = templateDataStoreVO.getInstallPath(); - if (validateIfSeeded(storeUrlAndId.first(), installPath)) { - continue; - } else if (templateVO != null) { - registerTemplate(hypervisorAndTemplateName, storeUrlAndId, templateVO, templateDataStoreVO, filePath); - continue; - } - } - registerTemplate(hypervisorAndTemplateName, storeUrlAndId, filePath); - } + registerTemplatesForZone(zoneId, filePath); 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(); @@ -809,83 +1046,331 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { } } - private void updateRegisteredTemplateDetails(Long templateId, Map.Entry hypervisorAndTemplateName) { + /** + * 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()); + if (guestOS != null) { + templateVO.setGuestOSId(guestOS.getId()); + } boolean updated = vmTemplateDao.update(templateVO.getId(), templateVO); if (!updated) { - String errMsg = String.format("updateSystemVmTemplates:Exception while updating template with id %s to be marked as 'system'", templateId); + String errMsg = String.format("Exception while updating template with id %s to be marked as 'system'", + templateId); LOGGER.error(errMsg); throw new CloudRuntimeException(errMsg); } - - updateSystemVMEntries(templateId, hypervisorAndTemplateName.getKey()); - - // Change value of global configuration parameter router.template.* for the corresponding hypervisor and minreq.sysvmtemplate.version for the ACS version - Map configParams = new HashMap<>(); - configParams.put(RouterTemplateConfigurationNames.get(hypervisorAndTemplateName.getKey()), hypervisorAndTemplateName.getValue()); - configParams.put("minreq.sysvmtemplate.version", getSystemVmTemplateVersion()); - updateConfigurationParams(configParams); + Hypervisor.HypervisorType hypervisorType = templateDetails.getHypervisorType(); + updateSystemVMEntries(templateId, hypervisorType); + updateConfigurationParams(hypervisorType, templateDetails.getName(), zoneId); } - private void updateTemplateUrlAndChecksum(VMTemplateVO templateVO, Map.Entry hypervisorAndTemplateName) { - templateVO.setUrl(NewTemplateUrl.get(hypervisorAndTemplateName.getKey())); - templateVO.setChecksum(NewTemplateChecksum.get(hypervisorAndTemplateName.getKey())); + protected void updateTemplateUrlChecksumAndGuestOsId(VMTemplateVO templateVO, + MetadataTemplateDetails templateDetails) { + templateVO.setUrl(templateDetails.getUrl()); + 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", hypervisorAndTemplateName.getKey().name()); + String errMsg = String.format("Exception while updating 'url' and 'checksum' for hypervisor type %s", + templateDetails.getHypervisorType()); LOGGER.error(errMsg); throw new CloudRuntimeException(errMsg); } } + /** + * 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) { + 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); + } + } + } + + /** + * 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"); + LOGGER.debug("Updating System VM templates"); + updateHypervisorGuestOsMap(); Transaction.execute(new TransactionCallbackNoReturn() { @Override public void doInTransactionWithoutResult(final TransactionStatus status) { - Set hypervisorsListInUse = new HashSet(); + List> hypervisorsInUse; try { - hypervisorsListInUse = clusterDao.getDistictAvailableHypervisorsAcrossClusters(); - + hypervisorsInUse = clusterDao.listDistinctHypervisorsAndArchExcludingExternalType(null); } catch (final Exception e) { - LOGGER.error("updateSystemVmTemplates: Exception caught while getting hypervisor types from clusters: " + e.getMessage()); - throw new CloudRuntimeException("updateSystemVmTemplates:Exception while getting hypervisor types from clusters", e); + throw new CloudRuntimeException("Exception while getting hypervisor types from clusters", e); } - - for (final Map.Entry hypervisorAndTemplateName : NewTemplateNameList.entrySet()) { - LOGGER.debug("Updating " + hypervisorAndTemplateName.getKey() + " System Vms"); - Long templateId = getRegisteredTemplateId(new Pair<>(hypervisorAndTemplateName.getKey(), hypervisorAndTemplateName.getValue())); + for (MetadataTemplateDetails templateDetails : METADATA_TEMPLATE_LIST) { try { - // change template type to SYSTEM - if (templateId != null) { - updateRegisteredTemplateDetails(templateId, hypervisorAndTemplateName); - } else { - if (hypervisorsListInUse.contains(hypervisorAndTemplateName.getKey())) { - try { - registerTemplates(hypervisorsListInUse); - break; - } catch (final Exception e) { - throw new CloudRuntimeException(String.format("%s %s SystemVm template not found. Cannot upgrade system Vms", getSystemVmTemplateVersion(), hypervisorAndTemplateName.getKey())); - } - } else { - LOGGER.warn(String.format("%s %s SystemVm template not found. Cannot upgrade system Vms hypervisor is not used, so not failing upgrade", - getSystemVmTemplateVersion(), hypervisorAndTemplateName.getKey())); - // Update the latest template URLs for corresponding hypervisor - VMTemplateVO templateVO = vmTemplateDao.findLatestTemplateByTypeAndHypervisor(hypervisorAndTemplateName.getKey(), Storage.TemplateType.SYSTEM); - if (templateVO != null) { - updateTemplateUrlAndChecksum(templateVO, hypervisorAndTemplateName); - } - } + if (updateOrRegisterSystemVmTemplate(templateDetails, hypervisorsInUse)) { + break; } } catch (final Exception e) { - String errMsg = "updateSystemVmTemplates:Exception while getting ids of templates"; + String errMsg = "Exception while registering/updating system VM Templates for hypervisors in metadata"; LOGGER.error(errMsg, e); throw new CloudRuntimeException(errMsg, e); } } - LOGGER.debug("Updating System Vm Template IDs Complete"); + LOGGER.debug("Updating System VM Templates Complete"); } }); } + + protected static class MetadataTemplateDetails { + private final Hypervisor.HypervisorType hypervisorType; + private final String name; + private final String filename; + private final String url; + private final String checksum; + private final CPU.CPUArch arch; + private String downloadedFilePath; + private final String guestOs; + + MetadataTemplateDetails(Hypervisor.HypervisorType hypervisorType, String name, String filename, String url, + String checksum, CPU.CPUArch arch, String guestOs) { + this.hypervisorType = hypervisorType; + this.name = name; + this.filename = filename; + this.url = url; + this.checksum = checksum; + this.arch = arch; + this.guestOs = guestOs; + } + + public Hypervisor.HypervisorType getHypervisorType() { + return hypervisorType; + } + + public String getName() { + return name; + } + + public String getFilename() { + return filename; + } + + public String getUrl() { + return url; + } + + public String getChecksum() { + return checksum; + } + + public CPU.CPUArch getArch() { + return arch; + } + + public String getGuestOs() { + return guestOs; + } + + public String getDownloadedFilePath() { + return downloadedFilePath; + } + + public void setDownloadedFilePath(String downloadedFilePath) { + this.downloadedFilePath = downloadedFilePath; + } + + public String getDefaultFilePath() { + return TEMPLATES_PATH + filename; + } + + public 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/DatabaseAccessObject.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/DatabaseAccessObject.java index 1c2c4b3c7ce7..223d7a466376 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/DatabaseAccessObject.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/DatabaseAccessObject.java @@ -87,6 +87,36 @@ public boolean columnExists(Connection conn, String tableName, String columnName return columnExists; } + public String getColumnType(Connection conn, String tableName, String columnName) { + try (PreparedStatement pstmt = conn.prepareStatement(String.format("DESCRIBE %s %s", tableName, columnName));){ + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) { + return rs.getString("Type"); + } + } catch (SQLException e) { + logger.warn("Type for column {} can not be retrieved in {} ignoring exception: {}", columnName, tableName, e.getMessage()); + } + return null; + } + + public void addColumn(Connection conn, String tableName, String columnName, String columnDefinition) { + try (PreparedStatement pstmt = conn.prepareStatement(String.format("ALTER TABLE %s ADD COLUMN %s %s", tableName, columnName, columnDefinition));){ + pstmt.executeUpdate(); + logger.debug("Column {} is added successfully from the table {}", columnName, tableName); + } catch (SQLException e) { + logger.warn("Unable to add column {} to table {} due to exception", columnName, tableName, e); + } + } + + public void changeColumn(Connection conn, String tableName, String oldColumnName, String newColumnName, String columnDefinition) { + try (PreparedStatement pstmt = conn.prepareStatement(String.format("ALTER TABLE %s CHANGE COLUMN %s %s %s", tableName, oldColumnName, newColumnName, columnDefinition));){ + pstmt.executeUpdate(); + logger.debug("Column {} is changed successfully to {} from the table {}", oldColumnName, newColumnName, tableName); + } catch (SQLException e) { + logger.warn("Unable to add column {} to {} from the table {} due to exception", oldColumnName, newColumnName, tableName, e); + } + } + public String generateIndexName(String tableName, String... columnName) { return String.format("i_%s__%s", tableName, StringUtils.join(columnName, "__")); } @@ -114,6 +144,17 @@ public void createIndex(Connection conn, String tableName, String indexName, Str } } + public void renameIndex(Connection conn, String tableName, String oldName, String newName) { + String stmt = String.format("ALTER TABLE %s RENAME INDEX %s TO %s", tableName, oldName, newName); + logger.debug("Statement: {}", stmt); + try (PreparedStatement pstmt = conn.prepareStatement(stmt)) { + pstmt.execute(); + logger.debug("Renamed index {} to {}", oldName, newName); + } catch (SQLException e) { + logger.warn("Unable to rename index {} to {}", oldName, newName, e); + } + } + protected void closePreparedStatement(PreparedStatement pstmt, String errorMessage) { try { if (pstmt != null) { diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgrade.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgrade.java index 02c401c81558..fa0d0506ac13 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgrade.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgrade.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.upgrade.dao; +import com.cloud.utils.exception.CloudRuntimeException; + import java.io.InputStream; import java.sql.Connection; @@ -24,18 +26,45 @@ public interface DbUpgrade { String getUpgradedVersion(); - boolean supportsRollingUpgrade(); + default boolean supportsRollingUpgrade() { + return false; + } /** * @return the script to prepare the database schema for the * data migration step. */ - InputStream[] getPrepareScripts(); + default InputStream[] getPrepareScripts() { + String fromVersion = getUpgradableVersionRange()[0]; + String toVersion = getUpgradableVersionRange()[1]; + final String scriptFile = String.format("META-INF/db/schema-%sto%s.sql", fromVersion.replace(".", ""), toVersion.replace(".", "")); + final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile); + if (script == null) { + throw new CloudRuntimeException("Unable to find " + scriptFile); + } + + return new InputStream[]{script}; + } /** * Performs the actual data migration. */ - void performDataMigration(Connection conn); + default void performDataMigration(Connection conn) { + } + + default InputStream[] getCleanupScripts() { + String fromVersion = getUpgradableVersionRange()[0]; + String toVersion = getUpgradableVersionRange()[1]; + final String scriptFile = String.format("META-INF/db/schema-%sto%s-cleanup.sql", fromVersion.replace(".", ""), toVersion.replace(".", "")); + final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile); + if (script == null) { + throw new CloudRuntimeException("Unable to find " + scriptFile); + } + + return new InputStream[]{script}; + } - InputStream[] getCleanupScripts(); + default boolean refreshPoolConnectionsAfterUpgrade() { + return false; + } } diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgradeSystemVmTemplate.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgradeSystemVmTemplate.java index 4211898adc7d..a8d2436672e7 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgradeSystemVmTemplate.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgradeSystemVmTemplate.java @@ -17,9 +17,23 @@ package com.cloud.upgrade.dao; +import com.cloud.upgrade.SystemVmTemplateRegistration; +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import java.sql.Connection; public interface DbUpgradeSystemVmTemplate { - void updateSystemVmTemplates(Connection conn); + default void updateSystemVmTemplates(Connection conn) { + Logger logger = LogManager.getLogger(getClass()); + logger.debug("Updating System Vm template IDs"); + try { + SystemVmTemplateRegistration systemVmTemplateRegistration = new SystemVmTemplateRegistration(""); + systemVmTemplateRegistration.updateSystemVmTemplates(conn); + } catch (Exception e) { + throw new CloudRuntimeException("Failed to find / register SystemVM template(s)"); + } + } } diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgradeUtils.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgradeUtils.java index 51e6ac7b9a1d..be073fcce770 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgradeUtils.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgradeUtils.java @@ -31,6 +31,12 @@ public static void addIndexIfNeeded(Connection conn, String tableName, String... } } + public static void renameIndexIfNeeded(Connection conn, String tableName, String oldName, String newName) { + if (!dao.indexExists(conn, tableName, oldName)) { + dao.renameIndex(conn, tableName, oldName, newName); + } + } + public static void addForeignKey(Connection conn, String tableName, String tableColumn, String foreignTableName, String foreignColumnName) { dao.addForeignKey(conn, tableName, tableColumn, foreignTableName, foreignColumnName); } @@ -52,4 +58,20 @@ public static void dropTableColumnsIfExist(Connection conn, String tableName, Li } } + public static String getTableColumnType(Connection conn, String tableName, String columnName) { + return dao.getColumnType(conn, tableName, columnName); + } + + public static void addTableColumnIfNotExist(Connection conn, String tableName, String columnName, String columnDefinition) { + if (!dao.columnExists(conn, tableName, columnName)) { + dao.addColumn(conn, tableName, columnName, columnDefinition); + } + } + + public static void changeTableColumnIfNotExist(Connection conn, String tableName, String oldColumnName, String newColumnName, String columnDefinition) { + if (dao.columnExists(conn, tableName, oldColumnName)) { + dao.changeColumn(conn, tableName, oldColumnName, newColumnName, columnDefinition); + } + } + } 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 0ca81be3936f..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 @@ -31,6 +31,13 @@ import com.cloud.hypervisor.Hypervisor; import com.cloud.utils.exception.CloudRuntimeException; +import static com.cloud.hypervisor.Hypervisor.HypervisorType.Hyperv; +import static com.cloud.hypervisor.Hypervisor.HypervisorType.KVM; +import static com.cloud.hypervisor.Hypervisor.HypervisorType.LXC; +import static com.cloud.hypervisor.Hypervisor.HypervisorType.Ovm3; +import static com.cloud.hypervisor.Hypervisor.HypervisorType.VMware; +import static com.cloud.hypervisor.Hypervisor.HypervisorType.XenServer; + public class Upgrade41500to41510 extends DbUpgradeAbstractImpl implements DbUpgradeSystemVmTemplate { @Override @@ -67,31 +74,23 @@ 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()) { - switch (Hypervisor.HypervisorType.getType(rs.getString(1))) { - case XenServer: - hypervisorsListInUse.add(Hypervisor.HypervisorType.XenServer); - break; - case KVM: - hypervisorsListInUse.add(Hypervisor.HypervisorType.KVM); - break; - case VMware: - hypervisorsListInUse.add(Hypervisor.HypervisorType.VMware); - break; - case Hyperv: - hypervisorsListInUse.add(Hypervisor.HypervisorType.Hyperv); - break; - case LXC: - hypervisorsListInUse.add(Hypervisor.HypervisorType.LXC); - break; - case Ovm3: - hypervisorsListInUse.add(Hypervisor.HypervisorType.Ovm3); - break; - default: - break; + Hypervisor.HypervisorType type = Hypervisor.HypervisorType.getType(rs.getString(1)); + if (type.equals(XenServer)) { + hypervisorsListInUse.add(XenServer); + } else if (type.equals(KVM)) { + hypervisorsListInUse.add(KVM); + } else if (type.equals(VMware)) { + hypervisorsListInUse.add(VMware); + } else if (type.equals(Hyperv)) { + hypervisorsListInUse.add(Hyperv); + } else if (type.equals(LXC)) { + hypervisorsListInUse.add(LXC); + } else if (type.equals(Ovm3)) { + hypervisorsListInUse.add(Ovm3); } } } catch (final SQLException e) { @@ -101,45 +100,45 @@ public void updateSystemVmTemplates(final Connection conn) { final Map NewTemplateNameList = new HashMap() { { - put(Hypervisor.HypervisorType.KVM, "systemvm-kvm-4.15.1"); - put(Hypervisor.HypervisorType.VMware, "systemvm-vmware-4.15.1"); - put(Hypervisor.HypervisorType.XenServer, "systemvm-xenserver-4.15.1"); - put(Hypervisor.HypervisorType.Hyperv, "systemvm-hyperv-4.15.1"); - put(Hypervisor.HypervisorType.LXC, "systemvm-lxc-4.15.1"); - put(Hypervisor.HypervisorType.Ovm3, "systemvm-ovm3-4.15.1"); + put(KVM, "systemvm-kvm-4.15.1"); + put(VMware, "systemvm-vmware-4.15.1"); + put(XenServer, "systemvm-xenserver-4.15.1"); + put(Hyperv, "systemvm-hyperv-4.15.1"); + put(LXC, "systemvm-lxc-4.15.1"); + put(Ovm3, "systemvm-ovm3-4.15.1"); } }; final Map routerTemplateConfigurationNames = new HashMap() { { - put(Hypervisor.HypervisorType.KVM, "router.template.kvm"); - put(Hypervisor.HypervisorType.VMware, "router.template.vmware"); - put(Hypervisor.HypervisorType.XenServer, "router.template.xenserver"); - put(Hypervisor.HypervisorType.Hyperv, "router.template.hyperv"); - put(Hypervisor.HypervisorType.LXC, "router.template.lxc"); - put(Hypervisor.HypervisorType.Ovm3, "router.template.ovm3"); + put(KVM, "router.template.kvm"); + put(VMware, "router.template.vmware"); + put(XenServer, "router.template.xenserver"); + put(Hyperv, "router.template.hyperv"); + put(LXC, "router.template.lxc"); + put(Ovm3, "router.template.ovm3"); } }; final Map newTemplateUrl = new HashMap() { { - put(Hypervisor.HypervisorType.KVM, "https://download.cloudstack.org/systemvm/4.15/systemvmtemplate-4.15.1-kvm.qcow2.bz2"); - put(Hypervisor.HypervisorType.VMware, "https://download.cloudstack.org/systemvm/4.15/systemvmtemplate-4.15.1-vmware.ova"); - put(Hypervisor.HypervisorType.XenServer, "https://download.cloudstack.org/systemvm/4.15/systemvmtemplate-4.15.1-xen.vhd.bz2"); - put(Hypervisor.HypervisorType.Hyperv, "https://download.cloudstack.org/systemvm/4.15/systemvmtemplate-4.15.1-hyperv.vhd.zip"); - put(Hypervisor.HypervisorType.LXC, "https://download.cloudstack.org/systemvm/4.15/systemvmtemplate-4.15.1-kvm.qcow2.bz2"); - put(Hypervisor.HypervisorType.Ovm3, "https://download.cloudstack.org/systemvm/4.15/systemvmtemplate-4.15.1-ovm.raw.bz2"); + put(KVM, "https://download.cloudstack.org/systemvm/4.15/systemvmtemplate-4.15.1-kvm.qcow2.bz2"); + put(VMware, "https://download.cloudstack.org/systemvm/4.15/systemvmtemplate-4.15.1-vmware.ova"); + put(XenServer, "https://download.cloudstack.org/systemvm/4.15/systemvmtemplate-4.15.1-xen.vhd.bz2"); + put(Hyperv, "https://download.cloudstack.org/systemvm/4.15/systemvmtemplate-4.15.1-hyperv.vhd.zip"); + put(LXC, "https://download.cloudstack.org/systemvm/4.15/systemvmtemplate-4.15.1-kvm.qcow2.bz2"); + put(Ovm3, "https://download.cloudstack.org/systemvm/4.15/systemvmtemplate-4.15.1-ovm.raw.bz2"); } }; final Map newTemplateChecksum = new HashMap() { { - put(Hypervisor.HypervisorType.KVM, "0e9f9a7d0957c3e0a2088e41b2da2cec"); - put(Hypervisor.HypervisorType.XenServer, "86373992740b1eca8aff8b08ebf3aea5"); - put(Hypervisor.HypervisorType.VMware, "4006982765846d373eb3719b2fe4d720"); - put(Hypervisor.HypervisorType.Hyperv, "0b9514e4b6cba1f636fea2125f0f7a5f"); - put(Hypervisor.HypervisorType.LXC, "0e9f9a7d0957c3e0a2088e41b2da2cec"); - put(Hypervisor.HypervisorType.Ovm3, "ae3977e696b3e6c81bdcbb792d514d29"); + put(KVM, "0e9f9a7d0957c3e0a2088e41b2da2cec"); + put(XenServer, "86373992740b1eca8aff8b08ebf3aea5"); + put(VMware, "4006982765846d373eb3719b2fe4d720"); + put(Hyperv, "0b9514e4b6cba1f636fea2125f0f7a5f"); + put(LXC, "0e9f9a7d0957c3e0a2088e41b2da2cec"); + put(Ovm3, "ae3977e696b3e6c81bdcbb792d514d29"); } }; @@ -154,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 @@ -164,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 @@ -174,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); } @@ -226,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 e3eb2bf514df..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 @@ -23,12 +23,16 @@ import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDaoImpl; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.commons.collections.CollectionUtils; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.dao.VolumeDaoImpl; import com.cloud.upgrade.SystemVmTemplateRegistration; +import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.exception.CloudRuntimeException; public class Upgrade41700to41710 extends DbUpgradeAbstractImpl implements DbUpgradeSystemVmTemplate { @@ -86,33 +90,67 @@ 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)"); } } - private void updateStorPoolStorageType() { - storageDao = new PrimaryDataStoreDaoImpl(); - List storPoolPools = storageDao.findPoolsByProvider("StorPool"); - for (StoragePoolVO storagePoolVO : storPoolPools) { - if (StoragePoolType.SharedMountPoint == storagePoolVO.getPoolType()) { - storagePoolVO.setPoolType(StoragePoolType.StorPool); - storageDao.update(storagePoolVO.getId(), storagePoolVO); - } - updateStorageTypeForStorPoolVolumes(storagePoolVO.getId()); + protected PrimaryDataStoreDao getStorageDao() { + if (storageDao == null) { + storageDao = new PrimaryDataStoreDaoImpl(); } + return storageDao; } - private void updateStorageTypeForStorPoolVolumes(long storagePoolId) { - volumeDao = new VolumeDaoImpl(); - List volumes = volumeDao.findByPoolId(storagePoolId, null); - for (VolumeVO volumeVO : volumes) { - volumeVO.setPoolType(StoragePoolType.StorPool); - volumeDao.update(volumeVO.getId(), volumeVO); + protected VolumeDao getVolumeDao() { + if (volumeDao == null) { + volumeDao = new VolumeDaoImpl(); } + return volumeDao; + } + + /* + GenericDao.customSearch using GenericSearchBuilder and GenericDao.update using + GenericDao.createSearchBuilder used here to prevent any future issues when new fields + are added to StoragePoolVO or VolumeVO and this upgrade path starts to fail. + */ + protected void updateStorPoolStorageType() { + StoragePoolVO pool = getStorageDao().createForUpdate(); + pool.setPoolType(StoragePoolType.StorPool); + SearchBuilder sb = getStorageDao().createSearchBuilder(); + sb.and("provider", sb.entity().getStorageProviderName(), SearchCriteria.Op.EQ); + sb.and("type", sb.entity().getPoolType(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("provider", StoragePoolType.StorPool.name()); + sc.setParameters("type", StoragePoolType.SharedMountPoint.name()); + getStorageDao().update(pool, sc); + + GenericSearchBuilder gSb = getStorageDao().createSearchBuilder(Long.class); + gSb.selectFields(gSb.entity().getId()); + gSb.and("provider", gSb.entity().getStorageProviderName(), SearchCriteria.Op.EQ); + gSb.done(); + SearchCriteria gSc = gSb.create(); + gSc.setParameters("provider", StoragePoolType.StorPool.name()); + List poolIds = getStorageDao().customSearch(gSc, null); + updateStorageTypeForStorPoolVolumes(poolIds); + } + + protected void updateStorageTypeForStorPoolVolumes(List storagePoolIds) { + if (CollectionUtils.isEmpty(storagePoolIds)) { + return; + } + VolumeVO volume = getVolumeDao().createForUpdate(); + volume.setPoolType(StoragePoolType.StorPool); + SearchBuilder sb = getVolumeDao().createSearchBuilder(); + sb.and("poolId", sb.entity().getPoolId(), SearchCriteria.Op.IN); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("poolId", storagePoolIds.toArray()); + getVolumeDao().update(volume, sc); } } 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/Upgrade41900to41910.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41900to41910.java new file mode 100644 index 000000000000..4eb8e9bdb2a6 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41900to41910.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 com.cloud.upgrade.dao; + +import com.cloud.upgrade.SystemVmTemplateRegistration; +import com.cloud.utils.exception.CloudRuntimeException; + +import java.io.InputStream; +import java.sql.Connection; + +public class Upgrade41900to41910 extends DbUpgradeAbstractImpl implements DbUpgrade, DbUpgradeSystemVmTemplate { + private SystemVmTemplateRegistration systemVmTemplateRegistration; + + @Override + public String[] getUpgradableVersionRange() { + return new String[]{"4.19.0.0", "4.19.1.0"}; + } + + @Override + public String getUpgradedVersion() { + return "4.19.1.0"; + } + + @Override + public boolean supportsRollingUpgrade() { + return false; + } + + @Override + public InputStream[] getPrepareScripts() { + final String scriptFile = "META-INF/db/schema-41900to41910.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) { + addIndexes(conn); + } + + @Override + public InputStream[] getCleanupScripts() { + final String scriptFile = "META-INF/db/schema-41900to41910-cleanup.sql"; + final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile); + if (script == null) { + throw new CloudRuntimeException("Unable to find " + scriptFile); + } + + return new InputStream[]{script}; + } + + private void addIndexes(Connection conn) { + DbUpgradeUtils.addIndexIfNeeded(conn, "vm_stats", "vm_id"); + } + + private void initSystemVmTemplateRegistration() { + systemVmTemplateRegistration = new SystemVmTemplateRegistration(""); + } + + @Override + public void updateSystemVmTemplates(Connection conn) { + 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)"); + } + } +} diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41900to42000.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41900to42000.java deleted file mode 100644 index 200c5fda2326..000000000000 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41900to42000.java +++ /dev/null @@ -1,83 +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 com.cloud.upgrade.dao; - -import java.io.InputStream; -import java.sql.Connection; - -import com.cloud.upgrade.SystemVmTemplateRegistration; -import com.cloud.utils.exception.CloudRuntimeException; - -public class Upgrade41900to42000 extends DbUpgradeAbstractImpl implements DbUpgrade, DbUpgradeSystemVmTemplate { - private SystemVmTemplateRegistration systemVmTemplateRegistration; - - @Override - public String[] getUpgradableVersionRange() { - return new String[] {"4.19.0.0", "4.20.0.0"}; - } - - @Override - public String getUpgradedVersion() { - return "4.20.0.0"; - } - - @Override - public boolean supportsRollingUpgrade() { - return false; - } - - @Override - public InputStream[] getPrepareScripts() { - final String scriptFile = "META-INF/db/schema-41900to42000.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() { - final String scriptFile = "META-INF/db/schema-41900to42000-cleanup.sql"; - final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile); - if (script == null) { - throw new CloudRuntimeException("Unable to find " + scriptFile); - } - - return new InputStream[] {script}; - } - - private void initSystemVmTemplateRegistration() { - systemVmTemplateRegistration = new SystemVmTemplateRegistration(""); - } - - @Override - public void updateSystemVmTemplates(Connection conn) { - 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)"); - } - } -} diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41910to41920.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41910to41920.java new file mode 100644 index 000000000000..6215021473ed --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41910to41920.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 com.cloud.upgrade.dao; + +import com.cloud.utils.exception.CloudRuntimeException; + +import java.io.InputStream; +import java.sql.Connection; + +public class Upgrade41910to41920 implements DbUpgrade { + + @Override + public String[] getUpgradableVersionRange() { + return new String[]{"4.19.1.0", "4.19.2.0"}; + } + + @Override + public String getUpgradedVersion() { + return "4.19.2.0"; + } + + @Override + public boolean supportsRollingUpgrade() { + return false; + } + + @Override + public InputStream[] getPrepareScripts() { + final String scriptFile = "META-INF/db/schema-41910to41920.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() { + final String scriptFile = "META-INF/db/schema-41910to41920-cleanup.sql"; + final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile); + if (script == null) { + throw new CloudRuntimeException("Unable to find " + scriptFile); + } + + return new InputStream[]{script}; + } +} diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41910to42000.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41910to42000.java new file mode 100644 index 000000000000..4c26c6bde50b --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41910to42000.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 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 com.cloud.upgrade.SystemVmTemplateRegistration; +import com.cloud.utils.exception.CloudRuntimeException; + +public class Upgrade41910to42000 extends DbUpgradeAbstractImpl implements DbUpgrade, DbUpgradeSystemVmTemplate { + private SystemVmTemplateRegistration systemVmTemplateRegistration; + private static final int MAX_INDEXED_CHARS_IN_CHAR_SET_UTF8MB4 = 191; + + @Override + public String[] getUpgradableVersionRange() { + return new String[] {"4.19.1.0", "4.20.0.0"}; + } + + @Override + public String getUpgradedVersion() { + return "4.20.0.0"; + } + + @Override + public boolean supportsRollingUpgrade() { + return false; + } + + @Override + public InputStream[] getPrepareScripts() { + final String scriptFile = "META-INF/db/schema-41910to42000.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) { + checkAndUpdateAffinityGroupNameCharSetToUtf8mb4(conn); + } + + @Override + public InputStream[] getCleanupScripts() { + final String scriptFile = "META-INF/db/schema-41910to42000-cleanup.sql"; + final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile); + if (script == null) { + throw new CloudRuntimeException("Unable to find " + scriptFile); + } + + return new InputStream[] {script}; + } + + private void initSystemVmTemplateRegistration() { + systemVmTemplateRegistration = new SystemVmTemplateRegistration(""); + } + + @Override + public void updateSystemVmTemplates(Connection conn) { + 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)"); + } + } + + private void checkAndUpdateAffinityGroupNameCharSetToUtf8mb4(Connection conn) { + logger.debug("Check and update char set for affinity group name to utf8mb4"); + try { + PreparedStatement pstmt = conn.prepareStatement("SELECT MAX(LENGTH(name)) FROM `cloud`.`affinity_group`"); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) { + long maxLengthOfName = rs.getLong(1); + if (maxLengthOfName <= MAX_INDEXED_CHARS_IN_CHAR_SET_UTF8MB4) { + pstmt = conn.prepareStatement(String.format("ALTER TABLE `cloud`.`affinity_group` MODIFY `name` VARCHAR(%d) CHARACTER SET utf8mb4 NOT NULL", MAX_INDEXED_CHARS_IN_CHAR_SET_UTF8MB4)); + pstmt.executeUpdate(); + logger.debug("Successfully updated char set for affinity group name to utf8mb4"); + } else { + logger.warn("Unable to update char set for affinity group name, as there are some names with more than " + MAX_INDEXED_CHARS_IN_CHAR_SET_UTF8MB4 + + " chars (max supported chars for index)"); + } + } + + if (rs != null && !rs.isClosed()) { + rs.close(); + } + if (pstmt != null && !pstmt.isClosed()) { + pstmt.close(); + } + } catch (final SQLException e) { + logger.warn("Exception while updating char set for affinity group name to utf8mb4: " + e.getMessage()); + } + } +} diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42000to42010.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42000to42010.java new file mode 100644 index 000000000000..0c0a9f070baa --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42000to42010.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 +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES 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.upgrade.SystemVmTemplateRegistration; +import com.cloud.utils.exception.CloudRuntimeException; + +public class Upgrade42000to42010 extends DbUpgradeAbstractImpl implements DbUpgrade, DbUpgradeSystemVmTemplate { + private SystemVmTemplateRegistration systemVmTemplateRegistration; + + @Override + public String[] getUpgradableVersionRange() { + return new String[] {"4.20.0.0", "4.20.1.0"}; + } + + @Override + public String getUpgradedVersion() { + return "4.20.1.0"; + } + + @Override + public boolean supportsRollingUpgrade() { + return false; + } + + @Override + public InputStream[] getPrepareScripts() { + final String scriptFile = "META-INF/db/schema-42000to42010.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) { + addIndexes(conn); + } + + @Override + public InputStream[] getCleanupScripts() { + final String scriptFile = "META-INF/db/schema-42000to42010-cleanup.sql"; + final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile); + if (script == null) { + throw new CloudRuntimeException("Unable to find " + scriptFile); + } + + return new InputStream[] {script}; + } + + private void initSystemVmTemplateRegistration() { + systemVmTemplateRegistration = new SystemVmTemplateRegistration(""); + } + + @Override + public void updateSystemVmTemplates(Connection conn) { + 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)", e); + } + } + + private void addIndexes(Connection conn) { + DbUpgradeUtils.addIndexIfNeeded(conn, "host", "mgmt_server_id"); + DbUpgradeUtils.addIndexIfNeeded(conn, "host", "resource"); + DbUpgradeUtils.addIndexIfNeeded(conn, "host", "resource_state"); + DbUpgradeUtils.addIndexIfNeeded(conn, "host", "type"); + + DbUpgradeUtils.renameIndexIfNeeded(conn, "user_ip_address", "public_ip_address", "uk_public_ip_address"); + DbUpgradeUtils.addIndexIfNeeded(conn, "user_ip_address", "public_ip_address"); + DbUpgradeUtils.addIndexIfNeeded(conn, "user_ip_address", "data_center_id"); + DbUpgradeUtils.addIndexIfNeeded(conn, "user_ip_address", "vlan_db_id"); + DbUpgradeUtils.addIndexIfNeeded(conn, "user_ip_address", "removed"); + + DbUpgradeUtils.addIndexIfNeeded(conn, "vlan", "vlan_type"); + DbUpgradeUtils.addIndexIfNeeded(conn, "vlan", "data_center_id"); + DbUpgradeUtils.addIndexIfNeeded(conn, "vlan", "removed"); + + DbUpgradeUtils.addIndexIfNeeded(conn, "network_offering_details", "name"); + + DbUpgradeUtils.addIndexIfNeeded(conn, "service_offering", "cpu"); + DbUpgradeUtils.addIndexIfNeeded(conn, "service_offering", "speed"); + DbUpgradeUtils.addIndexIfNeeded(conn, "service_offering", "ram_size"); + + DbUpgradeUtils.addIndexIfNeeded(conn, "op_host_planner_reservation", "resource_usage"); + + DbUpgradeUtils.addIndexIfNeeded(conn, "storage_pool", "pool_type"); + DbUpgradeUtils.addIndexIfNeeded(conn, "storage_pool", "data_center_id", "status", "scope", "hypervisor"); + + DbUpgradeUtils.addIndexIfNeeded(conn, "router_network_ref", "guest_type"); + + DbUpgradeUtils.addIndexIfNeeded(conn, "domain_router", "role"); + + DbUpgradeUtils.addIndexIfNeeded(conn, "async_job", "instance_type", "job_status"); + + DbUpgradeUtils.addIndexIfNeeded(conn, "cluster", "managed_state"); + } +} diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42010to42100.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42010to42100.java new file mode 100644 index 000000000000..786ee5afbc8e --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42010to42100.java @@ -0,0 +1,220 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES 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.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.cloudstack.framework.config.ConfigKey; + +import com.cloud.upgrade.SystemVmTemplateRegistration; +import com.cloud.utils.db.TransactionLegacy; +import com.cloud.utils.exception.CloudRuntimeException; + +public class Upgrade42010to42100 extends DbUpgradeAbstractImpl implements DbUpgrade, DbUpgradeSystemVmTemplate { + private SystemVmTemplateRegistration systemVmTemplateRegistration; + + @Override + public String[] getUpgradableVersionRange() { + return new String[] {"4.20.1.0", "4.21.0.0"}; + } + + @Override + public String getUpgradedVersion() { + return "4.21.0.0"; + } + + @Override + public boolean supportsRollingUpgrade() { + return false; + } + + @Override + public InputStream[] getPrepareScripts() { + final String scriptFile = "META-INF/db/schema-42010to42100.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) { + updateKubernetesClusterNodeVersions(conn); + migrateConfigurationScopeToBitmask(conn); + } + + @Override + public InputStream[] getCleanupScripts() { + final String scriptFile = "META-INF/db/schema-42010to42100-cleanup.sql"; + final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile); + if (script == null) { + throw new CloudRuntimeException("Unable to find " + scriptFile); + } + + return new InputStream[] {script}; + } + + private void initSystemVmTemplateRegistration() { + systemVmTemplateRegistration = new SystemVmTemplateRegistration(""); + } + + @Override + public void updateSystemVmTemplates(Connection conn) { + 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)"); + } + } + + private void updateKubernetesClusterNodeVersions(Connection conn) { + //get list of all non removed kubernetes clusters + try { + Map clusterAndVersion = getKubernetesClusterIdsAndVersion(conn); + updateKubernetesNodeVersions(conn, clusterAndVersion); + } catch (Exception e) { + String errMsg = "Failed to update kubernetes cluster nodes version"; + logger.error(errMsg); + throw new CloudRuntimeException(errMsg, e); + } + } + + private Map getKubernetesClusterIdsAndVersion(Connection conn) { + String listKubernetesClusters = "SELECT c.id, v.semantic_version FROM `cloud`.`kubernetes_cluster` c JOIN `cloud`.`kubernetes_supported_version` v ON (c.kubernetes_version_id = v.id) WHERE c.removed is NULL;"; + Map clusterAndVersion = new HashMap<>(); + try { + PreparedStatement pstmt = conn.prepareStatement(listKubernetesClusters); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + clusterAndVersion.put(rs.getLong(1), rs.getString(2)); + } + rs.close(); + pstmt.close(); + } catch (SQLException e) { + String errMsg = String.format("Failed to get all the kubernetes cluster ids due to: %s", e.getMessage()); + logger.error(errMsg); + throw new CloudRuntimeException(errMsg, e); + } + return clusterAndVersion; + } + + private void updateKubernetesNodeVersions(Connection conn, Map clusterAndVersion) { + List kubernetesClusterVmIds; + for (Map.Entry clusterVersionEntry : clusterAndVersion.entrySet()) { + try { + Long cksClusterId = clusterVersionEntry.getKey(); + String cksVersion = clusterVersionEntry.getValue(); + logger.debug(String.format("Adding CKS version %s to existing CKS cluster %s nodes", cksVersion, cksClusterId)); + kubernetesClusterVmIds = getKubernetesClusterVmMapIds(conn, cksClusterId); + updateKubernetesNodeVersion(conn, kubernetesClusterVmIds, cksClusterId, cksVersion); + } catch (Exception e) { + String errMsg = String.format("Failed to update the node version for kubernetes cluster nodes for the" + + " kubernetes cluster with id: %s," + + " due to: %s", clusterVersionEntry.getKey(), e.getMessage()); + logger.error(errMsg, e); + throw new CloudRuntimeException(errMsg, e); + } + } + } + + private List getKubernetesClusterVmMapIds(Connection conn, Long cksClusterId) { + List kubernetesClusterVmIds = new ArrayList<>(); + String getKubernetesClustersVmMap = "SELECT id FROM `cloud`.`kubernetes_cluster_vm_map` WHERE cluster_id = %s;"; + try { + PreparedStatement pstmt = conn.prepareStatement(String.format(getKubernetesClustersVmMap, cksClusterId)); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + kubernetesClusterVmIds.add(rs.getLong(1)); + } + rs.close(); + pstmt.close(); + } catch (SQLException e) { + String errMsg = String.format("Failed to get the kubernetes cluster vm map IDs for kubernetes cluster with id: %s," + + " due to: %s", cksClusterId, e.getMessage()); + logger.error(errMsg, e); + throw new CloudRuntimeException(errMsg, e); + } + return kubernetesClusterVmIds; + } + + private void updateKubernetesNodeVersion(Connection conn, List kubernetesClusterVmIds, Long cksClusterId, String cksVersion) { + String updateKubernetesNodeVersion = "UPDATE `cloud`.`kubernetes_cluster_vm_map` set kubernetes_node_version = ? WHERE id = ?;"; + for (Long nodeVmId : kubernetesClusterVmIds) { + try { + PreparedStatement pstmt = conn.prepareStatement(updateKubernetesNodeVersion); + pstmt.setString(1, cksVersion); + pstmt.setLong(2, nodeVmId); + pstmt.executeUpdate(); + pstmt.close(); + } catch (Exception e) { + String errMsg = String.format("Failed to update the node version for kubernetes cluster nodes for the" + + " kubernetes cluster with id: %s," + + " due to: %s", cksClusterId, e.getMessage()); + logger.error(errMsg, e); + throw new CloudRuntimeException(errMsg, e); + } + } + } + + protected void migrateConfigurationScopeToBitmask(Connection conn) { + String scopeDataType = DbUpgradeUtils.getTableColumnType(conn, "configuration", "scope"); + logger.info("Data type of the column scope of table configuration is {}", scopeDataType); + if (!"varchar(255)".equals(scopeDataType)) { + return; + } + DbUpgradeUtils.addTableColumnIfNotExist(conn, "configuration", "new_scope", "BIGINT DEFAULT 0"); + migrateExistingConfigurationScopeValues(conn); + DbUpgradeUtils.dropTableColumnsIfExist(conn, "configuration", List.of("scope")); + DbUpgradeUtils.changeTableColumnIfNotExist(conn, "configuration", "new_scope", "scope", "BIGINT NOT NULL DEFAULT 0 COMMENT 'Bitmask for scope(s) of this parameter'"); + } + + protected void migrateExistingConfigurationScopeValues(Connection conn) { + StringBuilder sql = new StringBuilder("UPDATE configuration\n" + + "SET new_scope = " + + " CASE "); + for (ConfigKey.Scope scope : ConfigKey.Scope.values()) { + sql.append(" WHEN scope = '").append(scope.name()).append("' THEN ").append(scope.getBitValue()).append(" "); + } + sql.append(" ELSE 0 " + + " END " + + "WHERE scope IS NOT NULL;"); + TransactionLegacy txn = TransactionLegacy.currentTxn(); + try (PreparedStatement pstmt = txn.prepareAutoCloseStatement(sql.toString())) { + pstmt.executeUpdate(); + } catch (SQLException e) { + logger.error("Failed to migrate existing configuration scope values to bitmask", e); + throw new CloudRuntimeException(String.format("Failed to migrate existing configuration scope values to bitmask due to: %s", e.getMessage())); + } + } + + @Override + public boolean refreshPoolConnectionsAfterUpgrade() { + return true; + } +} diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42020to42030.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42020to42030.java new file mode 100644 index 000000000000..68100e164018 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42020to42030.java @@ -0,0 +1,64 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.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; + } + + @Override + public void updateSystemVmTemplates(Connection conn) { + } +} diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42100to42200.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42100to42200.java new file mode 100644 index 000000000000..5138d51bb1b6 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42100to42200.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 +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES 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 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 + public String[] getUpgradableVersionRange() { + return new String[]{"4.21.0.0", "4.22.0.0"}; + } + + @Override + 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..c9610f7b9ff5 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42200to42210.java @@ -0,0 +1,30 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.upgrade.dao; + +public class 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"; + } +} 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..df4743894c9d --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42210to42300.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.upgrade.dao; + +import java.io.InputStream; + +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}; + } +} diff --git a/engine/schema/src/main/java/com/cloud/usage/UsageNetworksVO.java b/engine/schema/src/main/java/com/cloud/usage/UsageNetworksVO.java new file mode 100644 index 000000000000..1385e69da038 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/usage/UsageNetworksVO.java @@ -0,0 +1,143 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES 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.usage; + +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; + +import java.util.Date; + +@Entity +@Table(name = "usage_networks") +public class UsageNetworksVO implements InternalIdentity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "network_id") + private long networkId; + + @Column(name = "network_offering_id") + private long networkOfferingId; + + @Column(name = "zone_id") + private long zoneId; + + @Column(name = "account_id") + private long accountId; + + @Column(name = "domain_id") + private long domainId; + + @Column(name = "state") + private String state; + + @Column(name = "created") + @Temporal(value = TemporalType.TIMESTAMP) + private Date created = null; + + @Column(name = "removed") + @Temporal(value = TemporalType.TIMESTAMP) + private Date removed = null; + + protected UsageNetworksVO() { + } + + public UsageNetworksVO(long id, long networkId, long networkOfferingId, long zoneId, long accountId, long domainId, String state, Date created, Date removed) { + this.id = id; + this.networkId = networkId; + this.networkOfferingId = networkOfferingId; + this.zoneId = zoneId; + this.domainId = domainId; + this.accountId = accountId; + this.state = state; + this.created = created; + this.removed = removed; + } + + public UsageNetworksVO(long networkId, long networkOfferingId, long zoneId, long accountId, long domainId, String state, Date created, Date removed) { + this.networkId = networkId; + this.networkOfferingId = networkOfferingId; + this.zoneId = zoneId; + this.domainId = domainId; + this.accountId = accountId; + this.state = state; + this.created = created; + this.removed = removed; + } + + @Override + public long getId() { + return id; + } + + public long getZoneId() { + return zoneId; + } + + public long getAccountId() { + return accountId; + } + + public long getDomainId() { + return domainId; + } + + public long getNetworkId() { + return networkId; + } + + public void setNetworkId(long networkId) { + this.networkId = networkId; + } + + public long getNetworkOfferingId() { + return networkOfferingId; + } + + public void setNetworkOfferingId(long networkOfferingId) { + this.networkOfferingId = networkOfferingId; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public Date getCreated() { + return created; + } + + public Date getRemoved() { + return removed; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } +} diff --git a/engine/schema/src/main/java/com/cloud/usage/UsageVO.java b/engine/schema/src/main/java/com/cloud/usage/UsageVO.java index 10b295f593cf..b1f6d295fdd6 100644 --- a/engine/schema/src/main/java/com/cloud/usage/UsageVO.java +++ b/engine/schema/src/main/java/com/cloud/usage/UsageVO.java @@ -17,6 +17,7 @@ package com.cloud.usage; import java.util.Date; +import java.util.TimeZone; import javax.persistence.Column; import javax.persistence.Entity; @@ -27,9 +28,11 @@ import javax.persistence.Temporal; import javax.persistence.TemporalType; +import com.cloud.utils.DateUtil; import org.apache.cloudstack.api.InternalIdentity; import org.apache.cloudstack.usage.Usage; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; +import org.apache.commons.lang3.StringUtils; @Entity @Table(name = "cloud_usage") @@ -110,6 +113,9 @@ public class UsageVO implements Usage, InternalIdentity { @Column(name = "is_hidden") private boolean isHidden = false; + @Column(name = "state") + private String state; + public Integer getQuotaCalculated() { return quotaCalculated; } @@ -398,8 +404,22 @@ public void setHidden(boolean hidden) { this.isHidden = hidden; } + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + @Override public String toString() { - return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "usageId", "usageType", "startDate", "endDate"); + return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "usageId", "usageType"); + } + + public String toString(TimeZone timeZone) { + String startDateString = DateUtil.displayDateInTimezone(timeZone, getStartDate()); + String endDateString = DateUtil.displayDateInTimezone(timeZone, getEndDate()); + return String.format("%s,\"startDate\":\"%s\",\"endDate\":\"%s\"}", StringUtils.chop(this.toString()), startDateString, endDateString); } } 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/UsageVpcVO.java b/engine/schema/src/main/java/com/cloud/usage/UsageVpcVO.java new file mode 100644 index 000000000000..e676b2bc2e98 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/usage/UsageVpcVO.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 com.cloud.usage; + +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; + +import java.util.Date; + +@Entity +@Table(name = "usage_vpc") +public class UsageVpcVO implements InternalIdentity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "vpc_id") + private long vpcId; + + @Column(name = "zone_id") + private long zoneId; + + @Column(name = "account_id") + private long accountId; + + @Column(name = "domain_id") + private long domainId; + + + @Column(name = "state") + private String state; + + @Column(name = "created") + @Temporal(value = TemporalType.TIMESTAMP) + private Date created = null; + + @Column(name = "removed") + @Temporal(value = TemporalType.TIMESTAMP) + private Date removed = null; + + protected UsageVpcVO(){} + + public UsageVpcVO(long id, long vpcId, long zoneId, long accountId, long domainId, String state, Date created, Date removed) { + this.id = id; + this.vpcId = vpcId; + this.zoneId = zoneId; + this.domainId = domainId; + this.accountId = accountId; + this.state = state; + this.created = created; + this.removed = removed; + } + public UsageVpcVO(long vpcId, long zoneId, long accountId, long domainId, String state, Date created, Date removed) { + this.vpcId = vpcId; + this.zoneId = zoneId; + this.domainId = domainId; + this.accountId = accountId; + this.state = state; + this.created = created; + this.removed = removed; + } + + @Override + public long getId() { + return id; + } + + public long getZoneId() { + return zoneId; + } + + public long getAccountId() { + return accountId; + } + + public long getDomainId() { + return domainId; + } + + public long getVpcId() { + return vpcId; + } + + public void setVpcId(long vpcId) { + this.vpcId = vpcId; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public Date getCreated() { + return created; + } + + public Date getRemoved() { + return removed; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } +} diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageBackupDao.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageBackupDao.java index 8a72182ec677..2a24016653db 100644 --- a/engine/schema/src/main/java/com/cloud/usage/dao/UsageBackupDao.java +++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageBackupDao.java @@ -24,7 +24,7 @@ import com.cloud.utils.db.GenericDao; public interface UsageBackupDao extends GenericDao { - void updateMetrics(Long vmId, Long size, Long virtualSize); - void removeUsage(Long accountId, Long vmId, Date eventDate); + void updateMetrics(Long vmId, Long backupOfferingId, Long size, Long virtualSize); + void removeUsage(Long accountId, Long vmId, Long backupOfferingId, Date eventDate); List getUsageRecords(Long accountId, Date startDate, Date endDate); } diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageBackupDaoImpl.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageBackupDaoImpl.java index 3403a8dfe5bb..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 @@ -36,16 +36,17 @@ @Component public class UsageBackupDaoImpl extends GenericDaoBase implements UsageBackupDao { - protected static final String UPDATE_DELETED = "UPDATE usage_backup SET removed = ? WHERE account_id = ? AND vm_id = ? and removed IS NULL"; + protected static final String UPDATE_DELETED = "UPDATE usage_backup SET removed = ? WHERE account_id = ? AND vm_id = ? and backup_offering_id = ? and removed IS NULL"; protected static final String GET_USAGE_RECORDS_BY_ACCOUNT = "SELECT id, zone_id, account_id, domain_id, vm_id, backup_offering_id, size, protected_size, created, removed FROM usage_backup WHERE " + " account_id = ? AND ((removed IS NULL AND created <= ?) OR (created BETWEEN ? AND ?) OR (removed BETWEEN ? AND ?) " + " OR ((created <= ?) AND (removed >= ?)))"; @Override - public void updateMetrics(final Long vmId, final Long size, final Long virtualSize) { + public void updateMetrics(final Long vmId, Long backupOfferingId, final Long size, final Long virtualSize) { try (TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB)) { SearchCriteria sc = this.createSearchCriteria(); sc.addAnd("vmId", SearchCriteria.Op.EQ, vmId); + sc.addAnd("backupOfferingId", SearchCriteria.Op.EQ, backupOfferingId); UsageBackupVO vo = findOneBy(sc); if (vo != null) { vo.setSize(size); @@ -58,7 +59,7 @@ public void updateMetrics(final Long vmId, final Long size, final Long virtualSi } @Override - public void removeUsage(Long accountId, Long vmId, Date eventDate) { + public void removeUsage(Long accountId, Long vmId, Long backupOfferingId, Date eventDate) { TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); try { txn.start(); @@ -67,6 +68,7 @@ public void removeUsage(Long accountId, Long vmId, Date eventDate) { pstmt.setString(1, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), eventDate)); pstmt.setLong(2, accountId); pstmt.setLong(3, vmId); + 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 2335043b7c5f..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,6 +26,7 @@ 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; @@ -34,6 +35,7 @@ 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; @@ -50,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 (?,?,?,?,?,?,?,?,?,?, ?, ?, ?)"; @@ -87,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 @@ -106,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(); } @@ -128,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(); } @@ -368,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; } @@ -537,19 +543,17 @@ public void saveUsageRecords(List usageRecords) { } @Override - public void removeOldUsageRecords(int days) { - String sql = DELETE_ALL_BY_INTERVAL; + 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); - PreparedStatement pstmt = null; try { - txn.start(); - pstmt = txn.prepareAutoCloseStatement(sql); - pstmt.setLong(1, days); - pstmt.executeUpdate(); - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - logger.error("error removing old cloud_usage records for interval: " + days); + 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(); } 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 f22a906054d9..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(); @@ -37,4 +39,6 @@ public interface UsageJobDao extends GenericDao { UsageJobVO isOwner(String hostname, int pid); void updateJobSuccess(Long jobId, long startMillis, long endMillis, long execTime, boolean success); + + void removeLastOpenJobsOwned(String hostname, int pid); } diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageJobDaoImpl.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageJobDaoImpl.java index 6d460aadd093..6f340501cf18 100644 --- a/engine/schema/src/main/java/com/cloud/usage/dao/UsageJobDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageJobDaoImpl.java @@ -22,6 +22,7 @@ import java.util.List; +import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; import com.cloud.usage.UsageJobVO; @@ -114,7 +115,7 @@ public Long checkHeartbeat(String hostname, int pid, int aggregationDuration) { public UsageJobVO isOwner(String hostname, int pid) { TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); try { - if ((hostname == null) || (pid <= 0)) { + if (hostname == null || pid <= 0) { return null; } @@ -155,7 +156,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)); @@ -174,7 +176,7 @@ public UsageJobVO getNextImmediateJob() { SearchCriteria sc = createSearchCriteria(); sc.addAnd("endMillis", SearchCriteria.Op.EQ, Long.valueOf(0)); sc.addAnd("jobType", SearchCriteria.Op.EQ, Integer.valueOf(UsageJobVO.JOB_TYPE_SINGLE)); - sc.addAnd("scheduled", SearchCriteria.Op.EQ, Integer.valueOf(0)); + sc.addAnd("scheduled", SearchCriteria.Op.EQ, Integer.valueOf(UsageJobVO.JOB_NOT_SCHEDULED)); List jobs = search(sc, filter); if ((jobs == null) || jobs.isEmpty()) { @@ -194,4 +196,36 @@ public Date getLastHeartbeat() { } return jobs.get(0).getHeartbeat(); } + + private List getLastOpenJobsOwned(String hostname, int pid) { + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("endMillis", SearchCriteria.Op.EQ, Long.valueOf(0)); + sc.addAnd("host", SearchCriteria.Op.EQ, hostname); + if (pid > 0) { + sc.addAnd("pid", SearchCriteria.Op.EQ, Integer.valueOf(pid)); + } + return listBy(sc); + } + + @Override + public void removeLastOpenJobsOwned(String hostname, int pid) { + if (hostname == null) { + return; + } + + TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); + try { + List jobs = getLastOpenJobsOwned(hostname, pid); + if (CollectionUtils.isNotEmpty(jobs)) { + logger.info("Found {} opens job, to remove", jobs.size()); + for (UsageJobVO job : jobs) { + logger.debug("Removing job - id: {}, pid: {}, job type: {}, scheduled: {}, heartbeat: {}", + job.getId(), job.getPid(), job.getJobType(), job.getScheduled(), job.getHeartbeat()); + remove(job.getId()); + } + } + } finally { + txn.close(); + } + } } diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageNetworksDao.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageNetworksDao.java new file mode 100644 index 000000000000..4d84caaad44f --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageNetworksDao.java @@ -0,0 +1,33 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.usage.dao; + +import com.cloud.usage.UsageNetworksVO; +import com.cloud.utils.db.GenericDao; + +import java.util.Date; +import java.util.List; + +public interface UsageNetworksDao extends GenericDao { + void update(long networkId, long newNetworkOffering, String state); + + void remove(long networkId, Date removed); + + List getUsageRecords(Long accountId, Date startDate, Date endDate); + + List listAll(long networkId); +} diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageNetworksDaoImpl.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageNetworksDaoImpl.java new file mode 100644 index 000000000000..e7ae622ae54f --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageNetworksDaoImpl.java @@ -0,0 +1,151 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.usage.dao; + +import com.cloud.usage.UsageNetworksVO; +import com.cloud.utils.DateUtil; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.TransactionLegacy; + +import org.springframework.stereotype.Component; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.annotation.PostConstruct; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; + +@Component +public class UsageNetworksDaoImpl extends GenericDaoBase implements UsageNetworksDao { + private static final Logger LOGGER = LogManager.getLogger(UsageNetworksDaoImpl.class); + protected static final String GET_USAGE_RECORDS_BY_ACCOUNT = "SELECT id, network_id, network_offering_id, zone_id, account_id, domain_id, state, created, removed FROM usage_networks WHERE " + + " account_id = ? AND ((removed IS NULL AND created <= ?) OR (created BETWEEN ? AND ?) OR (removed BETWEEN ? AND ?) " + + " OR ((created <= ?) AND (removed >= ?)))"; + + private SearchBuilder usageNetworksSearch; + + @PostConstruct + public void init() { + usageNetworksSearch = createSearchBuilder(); + usageNetworksSearch.and("networkId", usageNetworksSearch.entity().getNetworkId(), SearchCriteria.Op.EQ); + usageNetworksSearch.done(); + } + + @Override + public void update(long networkId, long newNetworkOffering, String state) { + TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); + try { + SearchCriteria sc = this.createSearchCriteria(); + sc.addAnd("networkId", SearchCriteria.Op.EQ, networkId); + sc.addAnd("removed", SearchCriteria.Op.NULL); + UsageNetworksVO vo = findOneBy(sc); + if (vo != null) { + vo.setNetworkOfferingId(newNetworkOffering); + vo.setState(state); + update(vo.getId(), vo); + } + } catch (final Exception e) { + txn.rollback(); + LOGGER.error(String.format("Error updating usage of network due to [%s].", e.getMessage()), e); + } finally { + txn.close(); + } + } + + @Override + public void remove(long networkId, Date removed) { + TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); + try { + SearchCriteria sc = this.createSearchCriteria(); + sc.addAnd("networkId", SearchCriteria.Op.EQ, networkId); + sc.addAnd("removed", SearchCriteria.Op.NULL); + List usageNetworksVOs = listBy(sc); + for (UsageNetworksVO entry : usageNetworksVOs) { + entry.setRemoved(removed); + update(entry.getId(), entry); + } + } catch (final Exception e) { + txn.rollback(); + LOGGER.error(String.format("Error updating usage of network due to [%s].", e.getMessage()), e); + } finally { + txn.close(); + } + } + + @Override + public List getUsageRecords(Long accountId, Date startDate, Date endDate) { + List usageRecords = new ArrayList<>(); + TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); + PreparedStatement pstmt; + try { + int i = 1; + pstmt = txn.prepareAutoCloseStatement(GET_USAGE_RECORDS_BY_ACCOUNT); + pstmt.setLong(i++, accountId); + + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + long id = rs.getLong(1); + long networkId = rs.getLong(2); + long networkOfferingId = rs.getLong(3); + long zoneId = rs.getLong(4); + long acctId = rs.getLong(5); + long domId = rs.getLong(6); + String stateTS = rs.getString(7); + Date createdDate = null; + Date removedDate = null; + String createdTS = rs.getString(8); + String removedTS = rs.getString(9); + + if (createdTS != null) { + createdDate = DateUtil.parseDateString(s_gmtTimeZone, createdTS); + } + if (removedTS != null) { + removedDate = DateUtil.parseDateString(s_gmtTimeZone, removedTS); + } + usageRecords.add(new UsageNetworksVO(id, networkId, networkOfferingId, zoneId, acctId, domId, stateTS, createdDate, removedDate)); + } + } catch (Exception e) { + txn.rollback(); + LOGGER.warn("Error getting networks usage records", e); + } finally { + txn.close(); + } + + return usageRecords; + } + + @Override + public List listAll(long networkId) { + SearchCriteria sc = usageNetworksSearch.create(); + sc.setParameters("networkId", networkId); + return listBy(sc); + } +} diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/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/usage/dao/UsageVpcDao.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageVpcDao.java new file mode 100644 index 000000000000..5167bf88c485 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageVpcDao.java @@ -0,0 +1,33 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.usage.dao; + +import com.cloud.usage.UsageVpcVO; +import com.cloud.utils.db.GenericDao; + +import java.util.Date; +import java.util.List; + +public interface UsageVpcDao extends GenericDao { + void update(UsageVpcVO usage); + + void remove(long vpcId, Date removed); + + List getUsageRecords(Long accountId, Date startDate, Date endDate); + + List listAll(long vpcId); +} diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageVpcDaoImpl.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageVpcDaoImpl.java new file mode 100644 index 000000000000..b5d8e46ef092 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageVpcDaoImpl.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 com.cloud.usage.dao; + +import com.cloud.usage.UsageVpcVO; +import com.cloud.utils.DateUtil; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.TransactionLegacy; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; + +@Component +public class UsageVpcDaoImpl extends GenericDaoBase implements UsageVpcDao { + protected static final String GET_USAGE_RECORDS_BY_ACCOUNT = "SELECT id, vpc_id, zone_id, account_id, domain_id, state, created, removed FROM usage_vpc WHERE " + + " account_id = ? AND ((removed IS NULL AND created <= ?) OR (created BETWEEN ? AND ?) OR (removed BETWEEN ? AND ?) " + + " OR ((created <= ?) AND (removed >= ?)))"; + + private SearchBuilder usageVpcSearch; + + @PostConstruct + public void init() { + usageVpcSearch = createSearchBuilder(); + usageVpcSearch.and("vpcId", usageVpcSearch.entity().getVpcId(), SearchCriteria.Op.EQ); + usageVpcSearch.done(); + } + + @Override + public void update(UsageVpcVO usage) { + TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); + try { + SearchCriteria sc = this.createSearchCriteria(); + sc.addAnd("vpcId", SearchCriteria.Op.EQ, usage.getVpcId()); + sc.addAnd("created", SearchCriteria.Op.EQ, usage.getCreated()); + UsageVpcVO vo = findOneBy(sc); + if (vo != null) { + vo.setRemoved(usage.getRemoved()); + update(vo.getId(), vo); + } + } catch (final Exception e) { + logger.error(String.format("Error updating usage of VPC due to [%s].", e.getMessage()), e); + txn.rollback(); + } finally { + txn.close(); + } + } + + @Override + public void remove(long vpcId, Date removed) { + TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); + try { + SearchCriteria sc = this.createSearchCriteria(); + sc.addAnd("vpcId", SearchCriteria.Op.EQ, vpcId); + sc.addAnd("removed", SearchCriteria.Op.NULL); + List usageVpcVOs = listBy(sc); + for (UsageVpcVO entry : usageVpcVOs) { + entry.setRemoved(removed); + update(entry.getId(), entry); + } + } catch (final Exception e) { + txn.rollback(); + logger.error(String.format("Error updating usage of VPC due to [%s].", e.getMessage()), e); + } finally { + txn.close(); + } + } + + @Override + public List getUsageRecords(Long accountId, Date startDate, Date endDate) { + List usageRecords = new ArrayList<>(); + TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); + PreparedStatement pstmt; + try { + int i = 1; + pstmt = txn.prepareAutoCloseStatement(GET_USAGE_RECORDS_BY_ACCOUNT); + pstmt.setLong(i++, accountId); + + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate)); + pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate)); + + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + long id = rs.getLong(1); + long vpcId = rs.getLong(2); + long zoneId = rs.getLong(3); + long acctId = rs.getLong(4); + long domId = rs.getLong(5); + String stateTS = rs.getString(6); + Date createdDate = null; + Date removedDate = null; + String createdTS = rs.getString(7); + String removedTS = rs.getString(8); + + if (createdTS != null) { + createdDate = DateUtil.parseDateString(s_gmtTimeZone, createdTS); + } + if (removedTS != null) { + removedDate = DateUtil.parseDateString(s_gmtTimeZone, removedTS); + } + usageRecords.add(new UsageVpcVO(id, vpcId, zoneId, acctId, domId, stateTS, createdDate, removedDate)); + } + } catch (Exception e) { + txn.rollback(); + logger.warn("Error getting VPC usage records", e); + } finally { + txn.close(); + } + + return usageRecords; + } + + @Override + public List listAll(long vpcId) { + SearchCriteria sc = usageVpcSearch.create(); + sc.setParameters("vpcId", vpcId); + return listBy(sc); + } +} diff --git a/engine/schema/src/main/java/com/cloud/user/AccountDetailVO.java b/engine/schema/src/main/java/com/cloud/user/AccountDetailVO.java index 863f6c960083..aa6e49666dd4 100644 --- a/engine/schema/src/main/java/com/cloud/user/AccountDetailVO.java +++ b/engine/schema/src/main/java/com/cloud/user/AccountDetailVO.java @@ -23,18 +23,18 @@ import javax.persistence.Id; import javax.persistence.Table; -import org.apache.cloudstack.api.InternalIdentity; +import org.apache.cloudstack.api.ResourceDetail; @Entity @Table(name = "account_details") -public class AccountDetailVO implements InternalIdentity { +public class AccountDetailVO implements ResourceDetail { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private long id; @Column(name = "account_id") - private long accountId; + private long resourceId; @Column(name = "name") private String name; @@ -46,13 +46,14 @@ protected AccountDetailVO() { } public AccountDetailVO(long accountId, String name, String value) { - this.accountId = accountId; + this.resourceId = accountId; this.name = name; this.value = value; } - public long getAccountId() { - return accountId; + @Override + public long getResourceId() { + return resourceId; } public String getName() { @@ -63,6 +64,11 @@ 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/com/cloud/user/AccountDetailsDao.java b/engine/schema/src/main/java/com/cloud/user/AccountDetailsDao.java index f4534ee41ee2..65bbe1670a8b 100644 --- a/engine/schema/src/main/java/com/cloud/user/AccountDetailsDao.java +++ b/engine/schema/src/main/java/com/cloud/user/AccountDetailsDao.java @@ -19,8 +19,9 @@ import java.util.Map; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; -public interface AccountDetailsDao extends GenericDao { +public interface AccountDetailsDao extends GenericDao, ResourceDetailsDao { Map findDetails(long accountId); void persist(long accountId, Map details); diff --git a/engine/schema/src/main/java/com/cloud/user/AccountDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/user/AccountDetailsDaoImpl.java index 5451192fc6d7..cbacf9af5721 100644 --- a/engine/schema/src/main/java/com/cloud/user/AccountDetailsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/user/AccountDetailsDaoImpl.java @@ -26,22 +26,22 @@ import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.ConfigKey.Scope; import org.apache.cloudstack.framework.config.ScopedConfigStorage; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import com.cloud.domain.DomainDetailVO; import com.cloud.domain.DomainVO; -import com.cloud.domain.dao.DomainDetailsDao; import com.cloud.domain.dao.DomainDao; +import com.cloud.domain.dao.DomainDetailsDao; import com.cloud.user.dao.AccountDao; - -import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.Pair; import com.cloud.utils.db.QueryBuilder; 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 org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; -public class AccountDetailsDaoImpl extends GenericDaoBase implements AccountDetailsDao, ScopedConfigStorage { +public class AccountDetailsDaoImpl extends ResourceDetailsDaoBase implements AccountDetailsDao, ScopedConfigStorage { protected final SearchBuilder accountSearch; @Inject @@ -55,16 +55,16 @@ public class AccountDetailsDaoImpl extends GenericDaoBase protected AccountDetailsDaoImpl() { accountSearch = createSearchBuilder(); - accountSearch.and("accountId", accountSearch.entity().getAccountId(), Op.EQ); + accountSearch.and("accountId", accountSearch.entity().getResourceId(), Op.EQ); accountSearch.done(); } @Override public Map findDetails(long accountId) { QueryBuilder sc = QueryBuilder.create(AccountDetailVO.class); - sc.and(sc.entity().getAccountId(), Op.EQ, accountId); + sc.and(sc.entity().getResourceId(), Op.EQ, accountId); List results = sc.list(); - Map details = new HashMap(results.size()); + Map details = new HashMap<>(results.size()); for (AccountDetailVO r : results) { details.put(r.getName(), r.getValue()); } @@ -88,11 +88,16 @@ public void persist(long accountId, Map details) { @Override public AccountDetailVO findDetail(long accountId, String name) { QueryBuilder sc = QueryBuilder.create(AccountDetailVO.class); - sc.and(sc.entity().getAccountId(), Op.EQ, accountId); + sc.and(sc.entity().getResourceId(), Op.EQ, accountId); sc.and(sc.entity().getName(), Op.EQ, name); return sc.find(); } + @Override + public void addDetail(long resourceId, String key, String value, boolean display) { + super.addDetail(new AccountDetailVO(resourceId, key, value)); + } + @Override public void deleteDetails(long accountId) { SearchCriteria sc = accountSearch.create(); @@ -116,10 +121,10 @@ public Scope getScope() { } @Override - public String getConfigValue(long id, ConfigKey key) { + public String getConfigValue(long id, String key) { // check if account level setting is configured - AccountDetailVO vo = findDetail(id, key.key()); - String value = vo == null ? null : vo.getValue(); + AccountDetailVO vo = findDetail(id, key); + String value = vo == null ? null : getActualValue(vo); if (value != null) { return value; } @@ -138,9 +143,9 @@ public String getConfigValue(long id, ConfigKey key) { if (account.isPresent()) { DomainVO domain = _domainDao.findById(account.get().getDomainId()); while (domain != null) { - DomainDetailVO domainVO = _domainDetailsDao.findDetail(domain.getId(), key.key()); + DomainDetailVO domainVO = _domainDetailsDao.findDetail(domain.getId(), key); if (domainVO != null) { - value = domainVO.getValue(); + value = _domainDetailsDao.getActualValue(domainVO); break; } else if (domain.getParent() != null) { domain = _domainDao.findById(domain.getParent()); @@ -152,4 +157,13 @@ public String getConfigValue(long id, ConfigKey key) { } return value; } + + @Override + public Pair getParentScope(long id) { + Account account = _accountDao.findById(id); + if (account == null) { + return null; + } + return new Pair<>(getScope().getParent(), account.getDomainId()); + } } diff --git a/engine/schema/src/main/java/com/cloud/user/AccountVO.java b/engine/schema/src/main/java/com/cloud/user/AccountVO.java index f04b2bafbde6..74a538565d77 100644 --- a/engine/schema/src/main/java/com/cloud/user/AccountVO.java +++ b/engine/schema/src/main/java/com/cloud/user/AccountVO.java @@ -77,6 +77,9 @@ public class AccountVO implements Account { @Column(name = "default") boolean isDefault; + @Column(name = "api_key_access") + private Boolean apiKeyAccess; + public AccountVO() { uuid = UUID.randomUUID().toString(); } @@ -229,4 +232,14 @@ public String getName() { public String reflectionToString() { return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "uuid", "accountName", "domainId"); } + + @Override + public void setApiKeyAccess(Boolean apiKeyAccess) { + this.apiKeyAccess = apiKeyAccess; + } + + @Override + public Boolean getApiKeyAccess() { + return apiKeyAccess; + } } 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 c18ca53f7abe..7345eeb48539 100644 --- a/engine/schema/src/main/java/com/cloud/user/UserAccountVO.java +++ b/engine/schema/src/main/java/com/cloud/user/UserAccountVO.java @@ -17,6 +17,7 @@ package com.cloud.user; import java.util.Date; +import java.util.HashMap; import java.util.Map; import javax.persistence.Column; @@ -32,10 +33,10 @@ import javax.persistence.Transient; import org.apache.cloudstack.api.InternalIdentity; +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; -import org.apache.commons.lang3.StringUtils; @Entity @Table(name = "user") @@ -67,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; @@ -201,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; @@ -361,6 +333,9 @@ public void setUser2faProvider(String user2faProvider) { @Override public Map getDetails() { + if (details == null) { + details = new HashMap<>(); + } return details; } @@ -368,4 +343,10 @@ public Map getDetails() { public void setDetails(Map details) { this.details = details; } + + @Override + public String toString() { + return String.format("UserAccount %s.", ReflectionToStringBuilderUtils.reflectOnlySelectedFields + (this, "id", "uuid", "username", "accountName")); + } } diff --git a/engine/schema/src/main/java/com/cloud/user/UserDataVO.java b/engine/schema/src/main/java/com/cloud/user/UserDataVO.java index f54b1a8872ef..e8864976069d 100644 --- a/engine/schema/src/main/java/com/cloud/user/UserDataVO.java +++ b/engine/schema/src/main/java/com/cloud/user/UserDataVO.java @@ -24,8 +24,12 @@ import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; + +import java.util.Date; import java.util.UUID; +import com.cloud.utils.db.GenericDao; + @Entity @Table(name = "user_data") public class UserDataVO implements UserData { @@ -58,6 +62,12 @@ public UserDataVO() { @Column(name = "params", length = 4096) private String params; + @Column(name = GenericDao.REMOVED_COLUMN) + private Date removed; + + @Column(name = "for_cks") + private boolean forCks; + @Override public long getDomainId() { return domainId; @@ -98,6 +108,11 @@ public String getParams() { return params; } + @Override + public boolean isForCks() { + return forCks; + } + public void setAccountId(long accountId) { this.accountId = accountId; } @@ -117,4 +132,14 @@ public void setUserData(String userData) { public void setParams(String params) { this.params = params; } + + public void setRemoved(Date removed) { + this.removed = removed; + } + + public Date getRemoved() { + return removed; + } + + public void setForCks(boolean forCks) { this.forCks = forCks; } } 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 69970bf2d2cd..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; @@ -115,13 +107,16 @@ public class UserVO implements User, Identity, InternalIdentity { @Column(name = "key_for_2fa") private String keyFor2fa; + @Column(name = "api_key_access") + private Boolean apiKeyAccess; + public UserVO() { this.uuid = UUID.randomUUID().toString(); } 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) { @@ -147,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()); @@ -240,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)) { @@ -293,7 +266,7 @@ public void setRegistered(boolean registered) { @Override public String toString() { - return String.format("User %s.", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "username", "uuid")); + return String.format("User %s.", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "uuid", "username")); } @Override @@ -350,4 +323,13 @@ public void setUser2faProvider(String user2faProvider) { this.user2faProvider = user2faProvider; } + @Override + public void setApiKeyAccess(Boolean apiKeyAccess) { + this.apiKeyAccess = apiKeyAccess; + } + + @Override + public Boolean getApiKeyAccess() { + return apiKeyAccess; + } } 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 17b07496731e..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 @@ -16,23 +16,23 @@ // under the License. package com.cloud.user.dao; +import java.util.Date; +import java.util.List; + import com.cloud.user.Account; import com.cloud.user.AccountVO; -import com.cloud.user.User; import com.cloud.utils.Pair; import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDao; -import java.util.Date; -import java.util.List; - public interface AccountDao extends GenericDao { - Pair findUserAccountByApiKey(String apiKey); List findAccountsLike(String accountName); Pair, Integer> findAccountsLike(String accountName, Filter filter); + List findAccountsByName(String accountName); + List findActiveAccounts(Long maxAccountId, Filter filter); List findRecentlyDeletedAccounts(Long maxAccountId, Date earliestRemovedDate, Filter filter); diff --git a/engine/schema/src/main/java/com/cloud/user/dao/AccountDaoImpl.java b/engine/schema/src/main/java/com/cloud/user/dao/AccountDaoImpl.java index eed5572a0b24..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,13 +16,16 @@ // under the License. package com.cloud.user.dao; +import java.util.Date; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + import com.cloud.user.Account; import com.cloud.user.Account.State; import com.cloud.user.AccountVO; -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; @@ -30,20 +33,9 @@ import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Func; import com.cloud.utils.db.SearchCriteria.Op; -import org.apache.commons.lang3.StringUtils; -import com.cloud.utils.db.TransactionLegacy; -import org.springframework.stereotype.Component; - -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.util.Date; -import java.util.List; @Component public class AccountDaoImpl extends GenericDaoBase implements AccountDao { - private static final String FIND_USER_ACCOUNT_BY_API_KEY = "SELECT u.id, u.username, u.account_id, u.secret_key, u.state, " - + "a.id, a.account_name, a.type, a.role_id, a.domain_id, a.state " + "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; @@ -131,39 +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))); - - AccountVO a = new AccountVO(rs.getLong(6)); - a.setAccountName(rs.getString(7)); - a.setType(Account.Type.getFromValue(rs.getInt(8))); - a.setRoleId(rs.getLong(9)); - a.setDomainId(rs.getLong(10)); - a.setState(State.getValueOf(rs.getString(11))); - - 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(); @@ -178,6 +137,16 @@ public Pair, Integer> findAccountsLike(String accountName, Filte return searchAndCount(sc, filter); } + @Override + public List findAccountsByName(String accountName) { + SearchBuilder sb = createSearchBuilder(); + sb.and("accountName", sb.entity().getAccountName(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("accountName", accountName); + return search(sc, null); + } + @Override public Account findEnabledAccount(String accountName, Long domainId) { SearchCriteria sc = AllFieldsSearch.create("accountName", accountName); @@ -298,7 +267,7 @@ public void markForCleanup(long accountId) { if (!account.getNeedsCleanup()) { account.setNeedsCleanup(true); if (!update(accountId, account)) { - logger.warn("Failed to mark account id=" + accountId + " for cleanup"); + logger.warn("Failed to mark account {} for cleanup", account); } } } @@ -318,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/ConsoleSessionVO.java b/engine/schema/src/main/java/com/cloud/vm/ConsoleSessionVO.java index 81a11241e4be..d8f2838dd477 100644 --- a/engine/schema/src/main/java/com/cloud/vm/ConsoleSessionVO.java +++ b/engine/schema/src/main/java/com/cloud/vm/ConsoleSessionVO.java @@ -19,6 +19,8 @@ package com.cloud.vm; +import org.apache.cloudstack.consoleproxy.ConsoleSession; + import java.util.Date; import javax.persistence.Column; @@ -32,7 +34,7 @@ @Entity @Table(name = "console_session") -public class ConsoleSessionVO { +public class ConsoleSessionVO implements ConsoleSession { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -45,6 +47,9 @@ public class ConsoleSessionVO { @Column(name = "created") private Date created; + @Column(name = "domain_id") + private long domainId; + @Column(name = "account_id") private long accountId; @@ -64,6 +69,12 @@ public class ConsoleSessionVO { @Column(name = "removed") private Date removed; + @Column(name = "console_endpoint_creator_address") + private String consoleEndpointCreatorAddress; + + @Column(name = "client_address") + private String clientAddress; + public long getId() { return id; } @@ -80,6 +91,7 @@ public void setUuid(String uuid) { this.uuid = uuid; } + @Override public Date getCreated() { return created; } @@ -88,6 +100,16 @@ public void setCreated(Date created) { this.created = created; } + @Override + public long getDomainId() { + return domainId; + } + + public void setDomainId(long domainId) { + this.domainId = domainId; + } + + @Override public long getAccountId() { return accountId; } @@ -96,6 +118,7 @@ public void setAccountId(long accountId) { this.accountId = accountId; } + @Override public long getUserId() { return userId; } @@ -104,6 +127,7 @@ public void setUserId(long userId) { this.userId = userId; } + @Override public long getInstanceId() { return instanceId; } @@ -112,6 +136,7 @@ public void setInstanceId(long instanceId) { this.instanceId = instanceId; } + @Override public long getHostId() { return hostId; } @@ -120,6 +145,7 @@ public void setHostId(long hostId) { this.hostId = hostId; } + @Override public Date getRemoved() { return removed; } @@ -128,6 +154,7 @@ public void setRemoved(Date removed) { this.removed = removed; } + @Override public Date getAcquired() { return acquired; } @@ -135,4 +162,22 @@ public Date getAcquired() { public void setAcquired(Date acquired) { this.acquired = acquired; } + + @Override + public String getConsoleEndpointCreatorAddress() { + return consoleEndpointCreatorAddress; + } + + public void setConsoleEndpointCreatorAddress(String consoleEndpointCreatorAddress) { + this.consoleEndpointCreatorAddress = consoleEndpointCreatorAddress; + } + + @Override + public String getClientAddress() { + return clientAddress; + } + + public void setClientAddress(String clientAddress) { + this.clientAddress = clientAddress; + } } 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/InstanceGroupVO.java b/engine/schema/src/main/java/com/cloud/vm/InstanceGroupVO.java index 4437af29bc1d..d5bd8c5aaae9 100644 --- a/engine/schema/src/main/java/com/cloud/vm/InstanceGroupVO.java +++ b/engine/schema/src/main/java/com/cloud/vm/InstanceGroupVO.java @@ -32,6 +32,7 @@ import com.cloud.user.Account; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "instance_group") @@ -74,6 +75,12 @@ protected InstanceGroupVO() { super(); } + @Override + public String toString() { + return String.format("InstanceGroup %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "uuid", "name")); + } + + @Override public long getId() { return id; diff --git a/engine/schema/src/main/java/com/cloud/vm/ItWorkDao.java b/engine/schema/src/main/java/com/cloud/vm/ItWorkDao.java index 2d4a5e138fea..ab07d6989fae 100644 --- a/engine/schema/src/main/java/com/cloud/vm/ItWorkDao.java +++ b/engine/schema/src/main/java/com/cloud/vm/ItWorkDao.java @@ -41,5 +41,6 @@ public interface ItWorkDao extends GenericDao { boolean updateStep(ItWorkVO work, Step step); List listWorkInProgressFor(long nodeId); + int expungeByVmList(List vmIds, Long batchSize); } diff --git a/engine/schema/src/main/java/com/cloud/vm/ItWorkDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/ItWorkDaoImpl.java index ff727904dcb7..0cc0a0844431 100644 --- a/engine/schema/src/main/java/com/cloud/vm/ItWorkDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/ItWorkDaoImpl.java @@ -18,7 +18,7 @@ import java.util.List; - +import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; import com.cloud.utils.db.GenericDaoBase; @@ -103,4 +103,16 @@ public List listWorkInProgressFor(long nodeId) { return search(sc, null); } + + @Override + public int expungeByVmList(List vmIds, Long batchSize) { + if (CollectionUtils.isEmpty(vmIds)) { + return 0; + } + SearchBuilder sb = createSearchBuilder(); + sb.and("vmIds", sb.entity().getInstanceId(), SearchCriteria.Op.IN); + SearchCriteria sc = sb.create(); + sc.setParameters("vmIds", vmIds.toArray()); + return batchExpunge(sc, batchSize); + } } 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 a32a943ea585..6c569e22dd95 100644 --- a/engine/schema/src/main/java/com/cloud/vm/NicVO.java +++ b/engine/schema/src/main/java/com/cloud/vm/NicVO.java @@ -30,6 +30,7 @@ import javax.persistence.Table; import javax.persistence.Transient; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; @@ -329,17 +330,10 @@ public void setCreated(Date created) { @Override public String toString() { - return new StringBuilder("Nic[").append(id) - .append("-") - .append(instanceId) - .append("-") - .append(deviceId) - .append("-") - .append(reservationId) - .append("-") - .append(iPv4Address) - .append("]") - .toString(); + return String.format("Nic %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "instanceId", + "deviceId", "broadcastUri", "reservationId", "iPv4Address")); } @Override diff --git a/engine/schema/src/main/java/com/cloud/vm/UserVmDetailVO.java b/engine/schema/src/main/java/com/cloud/vm/UserVmDetailVO.java deleted file mode 100755 index 81bb6dd9d4f3..000000000000 --- a/engine/schema/src/main/java/com/cloud/vm/UserVmDetailVO.java +++ /dev/null @@ -1,90 +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 com.cloud.vm; - -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 = "user_vm_details") -public class UserVmDetailVO implements ResourceDetail { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "id") - private long id; - - @Column(name = "vm_id") - private long resourceId; - - @Column(name = "name") - private String name; - - @Column(name = "value", length = 5120) - private String value; - - @Column(name = "display") - private boolean display = true; - - public UserVmDetailVO() { - } - - public UserVmDetailVO(long vmId, String name, String value, boolean display) { - this.resourceId = vmId; - this.name = name; - this.value = value; - this.display = display; - } - - @Override - public long getId() { - return id; - } - - @Override - public String getName() { - return name; - } - - @Override - public String getValue() { - return value; - } - - @Override - public long getResourceId() { - return resourceId; - } - - @Override - public boolean isDisplay() { - return display; - } - - public void setName(String name) { - this.name = name; - } - - public void setValue(String value) { - this.value = value; - } -} diff --git a/engine/schema/src/main/java/com/cloud/vm/UserVmVO.java b/engine/schema/src/main/java/com/cloud/vm/UserVmVO.java index ce0bd2d57173..ce3a9a84a34f 100644 --- a/engine/schema/src/main/java/com/cloud/vm/UserVmVO.java +++ b/engine/schema/src/main/java/com/cloud/vm/UserVmVO.java @@ -148,6 +148,7 @@ public boolean isUpdateParameters() { return updateParameters; } + @Override public String getUserVmType() { return userVmType; } diff --git a/engine/schema/src/main/java/com/cloud/vm/VMInstanceDetailVO.java b/engine/schema/src/main/java/com/cloud/vm/VMInstanceDetailVO.java new file mode 100755 index 000000000000..7879aa24556b --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/vm/VMInstanceDetailVO.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 com.cloud.vm; + +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 = "vm_instance_details") +public class VMInstanceDetailVO implements ResourceDetail { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "vm_id") + private long resourceId; + + @Column(name = "name") + private String name; + + @Column(name = "value", length = 5120) + private String value; + + @Column(name = "display") + private boolean display = true; + + public VMInstanceDetailVO() { + } + + public VMInstanceDetailVO(long vmId, String name, String value, boolean display) { + this.resourceId = vmId; + this.name = name; + this.value = value; + this.display = display; + } + + @Override + public long getId() { + return id; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getValue() { + return value; + } + + @Override + public long getResourceId() { + return resourceId; + } + + @Override + public boolean isDisplay() { + return display; + } + + public void setName(String name) { + this.name = name; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java b/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java index adcf28a29b38..9d5e1b0ff500 100644 --- a/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java +++ b/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java @@ -26,6 +26,7 @@ import java.util.UUID; import javax.persistence.Column; +import javax.persistence.Convert; import javax.persistence.DiscriminatorColumn; import javax.persistence.DiscriminatorType; import javax.persistence.Entity; @@ -41,6 +42,7 @@ import javax.persistence.Transient; import org.apache.cloudstack.backup.Backup; +import org.apache.cloudstack.util.HypervisorTypeConverter; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import org.apache.commons.codec.binary.Base64; import org.apache.logging.log4j.Logger; @@ -159,16 +161,14 @@ public class VMInstanceVO implements VirtualMachine, FiniteStateObject details; @@ -503,7 +503,7 @@ public void setRemoved(Date removed) { @Override public String toString() { - return String.format("VM instance %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "instanceName", "uuid", "type")); + return String.format("VM instance %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "instanceName", "uuid", "type", "state")); } @Override @@ -540,6 +540,14 @@ public boolean isDynamicallyScalable() { return dynamicallyScalable; } + public boolean isDeleteProtection() { + return deleteProtection; + } + + public void setDeleteProtection(boolean deleteProtection) { + this.deleteProtection = deleteProtection; + } + @Override public Class getEntityType() { return VirtualMachine.class; diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleProxyDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleProxyDao.java index cb19748fda46..af32163b4c70 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleProxyDao.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleProxyDao.java @@ -45,7 +45,7 @@ public interface ConsoleProxyDao extends GenericDao { public List getDatacenterSessionLoadMatrix(); - public List> getDatacenterStoragePoolHostInfo(long dcId, boolean countAllPoolTypes); + public boolean hasDatacenterStoragePoolHostInfo(long dcId, boolean sharedOnly); public List> getProxyLoadMatrix(); diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleProxyDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleProxyDaoImpl.java index ef94a4d9f72e..bc79194a10fd 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleProxyDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleProxyDaoImpl.java @@ -23,7 +23,6 @@ import java.util.Date; import java.util.List; - import org.springframework.stereotype.Component; import com.cloud.info.ConsoleProxyLoadInfo; @@ -76,11 +75,11 @@ public class ConsoleProxyDaoImpl extends GenericDaoBase im private static final String GET_PROXY_ACTIVE_LOAD = "SELECT active_session AS count" + " FROM console_proxy" + " WHERE id=?"; - private static final String STORAGE_POOL_HOST_INFO = "SELECT p.data_center_id, count(ph.host_id) " + " FROM storage_pool p, storage_pool_host_ref ph " - + " WHERE p.id = ph.pool_id AND p.data_center_id = ? " + " GROUP by p.data_center_id"; + protected static final String STORAGE_POOL_HOST_INFO = "SELECT (SELECT id FROM storage_pool_host_ref ph WHERE " + + "ph.pool_id=p.id limit 1) AS sphr FROM storage_pool p WHERE p.data_center_id = ?"; - private static final String SHARED_STORAGE_POOL_HOST_INFO = "SELECT p.data_center_id, count(ph.host_id) " + " FROM storage_pool p, storage_pool_host_ref ph " - + " WHERE p.pool_type <> 'LVM' AND p.id = ph.pool_id AND p.data_center_id = ? " + " GROUP by p.data_center_id"; + protected static final String SHARED_STORAGE_POOL_HOST_INFO = "SELECT (SELECT id FROM storage_pool_host_ref ph " + + "WHERE ph.pool_id=p.id limit 1) AS sphr FROM storage_pool p WHERE p.data_center_id = ? AND p.pool_type NOT IN ('LVM', 'Filesystem')"; protected SearchBuilder DataCenterStatusSearch; protected SearchBuilder StateSearch; @@ -219,28 +218,23 @@ public List> getProxyLoadMatrix() { } @Override - public List> getDatacenterStoragePoolHostInfo(long dcId, boolean countAllPoolTypes) { - ArrayList> l = new ArrayList>(); - + public boolean hasDatacenterStoragePoolHostInfo(long dcId, boolean sharedOnly) { + Long poolCount = 0L; + String sql = sharedOnly ? SHARED_STORAGE_POOL_HOST_INFO : STORAGE_POOL_HOST_INFO; TransactionLegacy txn = TransactionLegacy.currentTxn(); - ; - PreparedStatement pstmt = null; - try { - if (countAllPoolTypes) { - pstmt = txn.prepareAutoCloseStatement(STORAGE_POOL_HOST_INFO); - } else { - pstmt = txn.prepareAutoCloseStatement(SHARED_STORAGE_POOL_HOST_INFO); - } + try (PreparedStatement pstmt = txn.prepareAutoCloseStatement(sql)) { pstmt.setLong(1, dcId); - ResultSet rs = pstmt.executeQuery(); while (rs.next()) { - l.add(new Pair(rs.getLong(1), rs.getInt(2))); + poolCount = rs.getLong(1); + if (poolCount > 0) { + return true; + } } } catch (SQLException e) { logger.debug("Caught SQLException: ", e); } - return l; + return false; } @Override diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleSessionDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleSessionDao.java index 71b1aed1938d..b8fb9557a356 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleSessionDao.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleSessionDao.java @@ -19,10 +19,12 @@ package com.cloud.vm.dao; +import com.cloud.utils.Pair; import com.cloud.vm.ConsoleSessionVO; import com.cloud.utils.db.GenericDao; import java.util.Date; +import java.util.List; public interface ConsoleSessionDao extends GenericDao { @@ -32,5 +34,12 @@ public interface ConsoleSessionDao extends GenericDao { int expungeSessionsOlderThanDate(Date date); - void acquireSession(String sessionUuid); + void acquireSession(String sessionUuid, String clientAddress); + + int expungeByVmList(List vmIds, Long batchSize); + + Pair, Integer> listConsoleSessions(Long id, List domainIds, Long accountId, Long userId, Long hostId, + Date startDate, Date endDate, Long instanceId, + String consoleEndpointCreatorAddress, String clientAddress, + boolean activeOnly, boolean acquired, Long pageSizeVal, Long startIndex); } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleSessionDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleSessionDaoImpl.java index 8e7e229622e8..562142eecc81 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleSessionDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleSessionDaoImpl.java @@ -20,6 +20,11 @@ package com.cloud.vm.dao; import java.util.Date; +import java.util.List; + +import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; +import org.apache.commons.collections.CollectionUtils; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; @@ -27,13 +32,28 @@ import com.cloud.vm.ConsoleSessionVO; public class ConsoleSessionDaoImpl extends GenericDaoBase implements ConsoleSessionDao { + private static final String ID = "id"; + private static final String DOMAIN_IDS = "domainIds"; + private static final String ACCOUNT_ID = "accountId"; + private static final String USER_ID = "userId"; + private static final String HOST_ID = "hostId"; + private static final String INSTANCE_ID = "instanceId"; + private static final String VM_IDS = "vmIds"; + private static final String START_DATE = "startDate"; + private static final String END_DATE = "endDate"; + private static final String CREATOR_ADDRESS = "creatorAddress"; + private static final String CLIENT_ADDRESS = "clientAddress"; + private static final String ACQUIRED = "acquired"; + private static final String CREATED = "created"; + private static final String REMOVED = "removed"; + private static final String REMOVED_NOT_NULL = "removedNotNull"; private final SearchBuilder searchByRemovedDate; public ConsoleSessionDaoImpl() { searchByRemovedDate = createSearchBuilder(); - searchByRemovedDate.and("removedNotNull", searchByRemovedDate.entity().getRemoved(), SearchCriteria.Op.NNULL); - searchByRemovedDate.and("removed", searchByRemovedDate.entity().getRemoved(), SearchCriteria.Op.LTEQ); + searchByRemovedDate.and(REMOVED_NOT_NULL, searchByRemovedDate.entity().getRemoved(), SearchCriteria.Op.NNULL); + searchByRemovedDate.and(REMOVED, searchByRemovedDate.entity().getRemoved(), SearchCriteria.Op.LTEQ); } @Override @@ -54,16 +74,84 @@ public boolean isSessionAllowed(String sessionUuid) { @Override public int expungeSessionsOlderThanDate(Date date) { SearchCriteria searchCriteria = searchByRemovedDate.create(); - searchCriteria.setParameters("removed", date); + searchCriteria.setParameters(REMOVED, date); return expunge(searchCriteria); } @Override - public void acquireSession(String sessionUuid) { + public void acquireSession(String sessionUuid, String clientAddress) { ConsoleSessionVO consoleSessionVO = findByUuid(sessionUuid); consoleSessionVO.setAcquired(new Date()); + consoleSessionVO.setClientAddress(clientAddress); update(consoleSessionVO.getId(), consoleSessionVO); } + @Override + public int expungeByVmList(List vmIds, Long batchSize) { + if (CollectionUtils.isEmpty(vmIds)) { + return 0; + } + SearchBuilder sb = createSearchBuilder(); + sb.and(VM_IDS, sb.entity().getInstanceId(), SearchCriteria.Op.IN); + SearchCriteria sc = sb.create(); + sc.setParameters(VM_IDS, vmIds.toArray()); + return batchExpunge(sc, batchSize); + } + + @Override + public Pair, Integer> listConsoleSessions(Long id, List domainIds, Long accountId, Long userId, Long hostId, + Date startDate, Date endDate, Long instanceId, + String consoleEndpointCreatorAddress, String clientAddress, + boolean activeOnly, boolean acquired, Long pageSizeVal, Long startIndex) { + Filter filter = new Filter(ConsoleSessionVO.class, CREATED, false, startIndex, pageSizeVal); + SearchCriteria searchCriteria = createListConsoleSessionsSearchCriteria(id, domainIds, accountId, userId, hostId, + startDate, endDate, instanceId, consoleEndpointCreatorAddress, clientAddress, activeOnly, acquired); + + return searchAndCount(searchCriteria, filter, true); + } + + private SearchCriteria createListConsoleSessionsSearchCriteria(Long id, List domainIds, Long accountId, Long userId, Long hostId, + Date startDate, Date endDate, Long instanceId, + String consoleEndpointCreatorAddress, String clientAddress, + boolean activeOnly, boolean acquired) { + SearchCriteria searchCriteria = createListConsoleSessionsSearchBuilder(activeOnly, acquired).create(); + searchCriteria.setParametersIfNotNull(ID, id); + searchCriteria.setParametersIfNotNull(DOMAIN_IDS, domainIds.toArray()); + searchCriteria.setParametersIfNotNull(ACCOUNT_ID, accountId); + searchCriteria.setParametersIfNotNull(USER_ID, userId); + searchCriteria.setParametersIfNotNull(HOST_ID, hostId); + searchCriteria.setParametersIfNotNull(INSTANCE_ID, instanceId); + searchCriteria.setParametersIfNotNull(START_DATE, startDate); + searchCriteria.setParametersIfNotNull(END_DATE, endDate); + searchCriteria.setParametersIfNotNull(CREATOR_ADDRESS, consoleEndpointCreatorAddress); + searchCriteria.setParametersIfNotNull(CLIENT_ADDRESS, clientAddress); + + return searchCriteria; + } + + private SearchBuilder createListConsoleSessionsSearchBuilder(boolean activeOnly, boolean acquired) { + SearchBuilder searchBuilder = createSearchBuilder(); + + searchBuilder.and(ID, searchBuilder.entity().getId(), SearchCriteria.Op.EQ); + searchBuilder.and(DOMAIN_IDS, searchBuilder.entity().getDomainId(), SearchCriteria.Op.IN); + searchBuilder.and(ACCOUNT_ID, searchBuilder.entity().getAccountId(), SearchCriteria.Op.EQ); + searchBuilder.and(USER_ID, searchBuilder.entity().getUserId(), SearchCriteria.Op.EQ); + searchBuilder.and(HOST_ID, searchBuilder.entity().getHostId(), SearchCriteria.Op.EQ); + searchBuilder.and(INSTANCE_ID, searchBuilder.entity().getInstanceId(), SearchCriteria.Op.EQ); + searchBuilder.and(START_DATE, searchBuilder.entity().getCreated(), SearchCriteria.Op.GTEQ); + searchBuilder.and(END_DATE, searchBuilder.entity().getCreated(), SearchCriteria.Op.LTEQ); + searchBuilder.and(CREATOR_ADDRESS, searchBuilder.entity().getConsoleEndpointCreatorAddress(), SearchCriteria.Op.EQ); + searchBuilder.and(CLIENT_ADDRESS, searchBuilder.entity().getClientAddress(), SearchCriteria.Op.EQ); + + if (activeOnly) { + searchBuilder.and(ACQUIRED, searchBuilder.entity().getAcquired(), SearchCriteria.Op.NNULL); + searchBuilder.and(REMOVED, searchBuilder.entity().getRemoved(), SearchCriteria.Op.NULL); + } else if (acquired) { + searchBuilder.and(ACQUIRED, searchBuilder.entity().getAcquired(), SearchCriteria.Op.NNULL); + } + + searchBuilder.done(); + return searchBuilder; + } } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/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 68f57329d779..96a1b6e3bd16 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 @@ -44,8 +44,14 @@ public interface NicDao extends GenericDao { NicVO findByNetworkIdAndType(long networkId, VirtualMachine.Type vmType); + NicVO findByNetworkIdAndTypeIncludingRemoved(long networkId, VirtualMachine.Type vmType); + + NicVO findNonPlaceHolderByNetworkIdAndType(long networkId, VirtualMachine.Type vmType); + NicVO findByIp4AddressAndNetworkId(String ip4Address, long networkId); + NicVO findNonPlaceHolderByIp4AddressAndNetworkId(String ip4Address, long networkId); + NicVO findByNetworkIdAndMacAddress(long networkId, String mac); NicVO findDefaultNicForVM(long instanceId); @@ -89,7 +95,7 @@ public interface NicDao extends GenericDao { List listByVmIdAndKeyword(long instanceId, String keyword); - NicVO findByMacAddress(String macAddress); + NicVO findByMacAddress(String macAddress, long networkId); NicVO findByNetworkIdAndMacAddressIncludingRemoved(long networkId, String mac); @@ -98,4 +104,5 @@ public interface NicDao extends GenericDao { NicVO findByIpAddressAndVmType(String ip, VirtualMachine.Type vmType); List listByNetworkIdAndType(long networkId, VirtualMachine.Type vmType); + List searchRemovedByVms(List vmIds, Long batchSize); } 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 59d2417b073c..78966a09e97c 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 @@ -17,11 +17,13 @@ package com.cloud.vm.dao; import java.net.URI; +import java.util.ArrayList; import java.util.List; import javax.annotation.PostConstruct; import javax.inject.Inject; +import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; import com.cloud.utils.db.Filter; @@ -67,6 +69,7 @@ protected void init() { AllFieldsSearch.and("secondaryip", AllFieldsSearch.entity().getSecondaryIp(), Op.EQ); AllFieldsSearch.and("nicid", AllFieldsSearch.entity().getId(), Op.EQ); AllFieldsSearch.and("strategy", AllFieldsSearch.entity().getReservationStrategy(), Op.EQ); + AllFieldsSearch.and("strategyNEQ", AllFieldsSearch.entity().getReservationStrategy(), Op.NEQ); AllFieldsSearch.and("reserverName",AllFieldsSearch.entity().getReserver(),Op.EQ); AllFieldsSearch.and("macAddress", AllFieldsSearch.entity().getMacAddress(), Op.EQ); AllFieldsSearch.and("deviceid", AllFieldsSearch.entity().getDeviceId(), Op.EQ); @@ -176,11 +179,29 @@ public NicVO findByInstanceIdAndNetworkIdIncludingRemoved(long networkId, long i return findOneIncludingRemovedBy(sc); } + private NicVO findByNetworkIdAndTypeInternal(long networkId, VirtualMachine.Type vmType, boolean includingRemoved) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("network", networkId); + sc.setParameters("vmType", vmType); + return includingRemoved ? findOneIncludingRemovedBy(sc) : findOneBy(sc); + } + @Override public NicVO findByNetworkIdAndType(long networkId, VirtualMachine.Type vmType) { + return findByNetworkIdAndTypeInternal(networkId, vmType, false); + } + + @Override + public NicVO findByNetworkIdAndTypeIncludingRemoved(long networkId, VirtualMachine.Type vmType) { + return findByNetworkIdAndTypeInternal(networkId, vmType, true); + } + + @Override + public NicVO findNonPlaceHolderByNetworkIdAndType(long networkId, VirtualMachine.Type vmType) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("network", networkId); sc.setParameters("vmType", vmType); + sc.setParameters("strategyNEQ", Nic.ReservationStrategy.PlaceHolder.toString()); return findOneBy(sc); } @@ -211,6 +232,16 @@ public NicVO findByIp4AddressAndNetworkId(String ip4Address, long networkId) { return findOneBy(sc); } + @Override + public NicVO findNonPlaceHolderByIp4AddressAndNetworkId(String ip4Address, long networkId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("address", ip4Address); + sc.setParameters("network", networkId); + sc.setParameters("strategyNEQ", Nic.ReservationStrategy.PlaceHolder.toString()); + return findOneBy(sc); + } + + @Override public NicVO findByNetworkIdAndMacAddress(long networkId, String mac) { SearchCriteria sc = AllFieldsSearch.create(); @@ -389,9 +420,10 @@ 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); } @@ -419,4 +451,18 @@ public List listByNetworkIdAndType(long networkId, VirtualMachine.Type vm sc.setParameters("vmType", vmType); return listBy(sc); } + + @Override + public List searchRemovedByVms(List vmIds, Long batchSize) { + if (CollectionUtils.isEmpty(vmIds)) { + return new ArrayList<>(); + } + SearchBuilder sb = createSearchBuilder(); + sb.and("vmIds", sb.entity().getInstanceId(), SearchCriteria.Op.IN); + sb.and("removed", sb.entity().getRemoved(), SearchCriteria.Op.NNULL); + SearchCriteria sc = sb.create(); + sc.setParameters("vmIds", vmIds.toArray()); + Filter filter = new Filter(NicVO.class, "id", true, 0L, batchSize); + return searchIncludingRemoved(sc, filter, null, false); + } } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicExtraDhcpOptionDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicExtraDhcpOptionDao.java index 69d9c00e1e0b..7bae64a6acbd 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/NicExtraDhcpOptionDao.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicExtraDhcpOptionDao.java @@ -29,4 +29,5 @@ public interface NicExtraDhcpOptionDao extends GenericDao extraDhcpOptions); + int expungeByNicList(List nicIds, Long batchSize); } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicExtraDhcpOptionDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicExtraDhcpOptionDaoImpl.java index 3056c73938e7..0f3679d66a37 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/NicExtraDhcpOptionDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicExtraDhcpOptionDaoImpl.java @@ -16,13 +16,13 @@ // under the License. package com.cloud.vm.dao; -import org.springframework.stereotype.Component; - import java.util.List; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.stereotype.Component; + import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; - import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.vm.NicExtraDhcpOption; @@ -74,4 +74,15 @@ public void removeByNicId(long nicId) { expunge(sc); } + @Override + public int expungeByNicList(List nicIds, Long batchSize) { + if (CollectionUtils.isEmpty(nicIds)) { + return 0; + } + SearchBuilder sb = createSearchBuilder(); + sb.and("nicIds", sb.entity().getNicId(), SearchCriteria.Op.IN); + SearchCriteria sc = sb.create(); + sc.setParameters("nicIds", nicIds.toArray()); + return batchExpunge(sc, batchSize); + } } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicIpAliasDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicIpAliasDaoImpl.java index 887b3d73087e..44866c0a3585 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/NicIpAliasDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicIpAliasDaoImpl.java @@ -170,8 +170,7 @@ public NicIpAliasVO findByIp4AddressAndNetworkIdAndInstanceId(long networkId, Lo public Integer countAliasIps(long id) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("instanceId", id); - List list = listBy(sc); - return list.size(); + return getCount(sc); } @Override diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicSecondaryIpDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicSecondaryIpDao.java index cbb52e57282b..ff7089ca4276 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/NicSecondaryIpDao.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicSecondaryIpDao.java @@ -55,4 +55,5 @@ public interface NicSecondaryIpDao extends GenericDao { List listSecondaryIpUsingKeyword(long nicId, String keyword); int moveSecondaryIps(long fromNicId, long toNicId); + int expungeByVmList(List vmIds, Long batchSize); } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicSecondaryIpDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicSecondaryIpDaoImpl.java index a56d35d5a63d..563b3279520c 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/NicSecondaryIpDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicSecondaryIpDaoImpl.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.List; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; @@ -192,4 +193,16 @@ public int moveSecondaryIps(long fromNicId, long toNicId) { return update(update, sc); } + + @Override + public int expungeByVmList(List vmIds, Long batchSize) { + if (CollectionUtils.isEmpty(vmIds)) { + return 0; + } + SearchBuilder sb = createSearchBuilder(); + sb.and("vmIds", sb.entity().getVmId(), SearchCriteria.Op.IN); + SearchCriteria sc = sb.create(); + sc.setParameters("vmIds", vmIds.toArray()); + return batchExpunge(sc, batchSize); + } } 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 093434052bc1..4c8208b4be84 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 @@ -28,6 +28,7 @@ import com.cloud.utils.db.GenericDao; import com.cloud.vm.NicSecondaryIp; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "nic_secondary_ips") @@ -87,6 +88,14 @@ protected NicSecondaryIpVO() { @Column(name = "vmId") long vmId; + @Override + public String toString() { + return String.format("NicSecondaryIp %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "vmId", + "nicId", "ip4Address", "ip6Address", "networkId")); + } + @Override public long getId() { return id; diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDao.java index 39c65866658f..7de543e69d31 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDao.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDao.java @@ -53,7 +53,11 @@ public interface UserVmDao extends GenericDao { * @param hostName TODO * @param instanceName */ - void updateVM(long id, String displayName, boolean enable, Long osTypeId, String userData, Long userDataId, String userDataDetails, boolean displayVm, boolean isDynamicallyScalable, String customId, String hostName, String instanceName); + void updateVM(long id, String displayName, boolean enable, Long osTypeId, + String userData, Long userDataId, String userDataDetails, + boolean displayVm, boolean isDynamicallyScalable, + boolean deleteProtection, String customId, String hostName, + String instanceName); List findDestroyedVms(Date date); 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 f4ce01afef34..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 @@ -26,10 +26,16 @@ 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 com.cloud.configuration.Resource; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallback; +import org.apache.cloudstack.reservation.ReservationVO; +import org.apache.cloudstack.reservation.dao.ReservationDao; import org.apache.commons.collections.CollectionUtils; import com.cloud.network.Network; @@ -51,7 +57,7 @@ import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.NicVO; -import com.cloud.vm.UserVmDetailVO; +import com.cloud.vm.VMInstanceDetailVO; import com.cloud.vm.UserVmVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.State; @@ -91,9 +97,11 @@ public class UserVmDaoImpl extends GenericDaoBase implements Use NetworkDao networkDao; @Inject NetworkOfferingServiceMapDao networkOfferingServiceMapDao; + @Inject + 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, " @@ -116,13 +124,13 @@ public class UserVmDaoImpl extends GenericDaoBase implements Use + "left join security_group on security_group_vm_map.security_group_id=security_group.id " + "left join nics on vm_instance.id=nics.instance_id " + "left join networks on nics.network_id=networks.id " + "left join user_ip_address on user_ip_address.vm_id=vm_instance.id " + "where vm_instance.id in ("; - private static final String VMS_DETAIL_BY_NAME = "select vm_instance.instance_name, vm_instance.vm_type, vm_instance.id , user_vm_details.value, user_vm_details.name from vm_instance " - + "left join user_vm_details on vm_instance.id = user_vm_details.vm_id where (user_vm_details.name is null or user_vm_details.name = ? ) and vm_instance.instance_name in ("; + private static final String VMS_DETAIL_BY_NAME = "select vm_instance.instance_name, vm_instance.vm_type, vm_instance.id , vm_instance_details.value, vm_instance_details.name from vm_instance " + + "left join vm_instance_details on vm_instance.id = vm_instance_details.vm_id where (vm_instance_details.name is null or vm_instance_details.name = ? ) and vm_instance.instance_name in ("; private static final int VM_DETAILS_BATCH_SIZE = 100; @Inject - protected UserVmDetailsDao _detailsDao; + protected VMInstanceDetailsDao _detailsDao; @Inject protected NicDao _nicDao; @@ -198,6 +206,7 @@ void init() { CountByAccount.and("type", CountByAccount.entity().getType(), SearchCriteria.Op.EQ); CountByAccount.and("state", CountByAccount.entity().getState(), SearchCriteria.Op.NIN); CountByAccount.and("displayVm", CountByAccount.entity().isDisplayVm(), SearchCriteria.Op.EQ); + CountByAccount.and("idNIN", CountByAccount.entity().getId(), SearchCriteria.Op.NIN); CountByAccount.done(); CountActiveAccount = createSearchBuilder(Long.class); @@ -265,8 +274,11 @@ public List listByAccountAndDataCenter(long accountId, long dcId) { } @Override - public void updateVM(long id, String displayName, boolean enable, Long osTypeId, String userData, Long userDataId, String userDataDetails, boolean displayVm, - boolean isDynamicallyScalable, String customId, String hostName, String instanceName) { + public void updateVM(long id, String displayName, boolean enable, Long osTypeId, + String userData, Long userDataId, String userDataDetails, + boolean displayVm, boolean isDynamicallyScalable, + boolean deleteProtection, String customId, String hostName, + String instanceName) { UserVmVO vo = createForUpdate(); vo.setDisplayName(displayName); vo.setHaEnabled(enable); @@ -276,6 +288,7 @@ public void updateVM(long id, String displayName, boolean enable, Long osTypeId, vo.setUserDataDetails(userDataDetails); vo.setDisplayVm(displayVm); vo.setDynamicallyScalable(isDynamicallyScalable); + vo.setDeleteProtection(deleteProtection); if (hostName != null) { vo.setHostName(hostName); } @@ -432,10 +445,10 @@ public void saveDetails(UserVmVO vm, List hiddenDetails) { final Map visibilityMap = _detailsDao.listDetailsVisibility(vm.getId()); - List details = new ArrayList(); + List details = new ArrayList(); for (Map.Entry entry : detailsStr.entrySet()) { boolean display = !hiddenDetails.contains(entry.getKey()) && visibilityMap.getOrDefault(entry.getKey(), true); - details.add(new UserVmDetailVO(vm.getId(), entry.getKey(), entry.getValue(), display)); + details.add(new VMInstanceDetailVO(vm.getId(), entry.getKey(), entry.getValue(), display)); } _detailsDao.saveDetails(details); @@ -697,6 +710,9 @@ public String getQueryBatchAppender(int count) { @Override public Long countAllocatedVMsForAccount(long accountId, boolean runningVMsonly) { + List reservations = reservationDao.getReservationsForAccount(accountId, Resource.ResourceType.user_vm, null); + List reservedResourceIds = reservations.stream().filter(reservation -> reservation.getReservedAmount() > 0).map(ReservationVO::getResourceId).collect(Collectors.toList()); + SearchCriteria sc = CountByAccount.create(); sc.setParameters("account", accountId); sc.setParameters("type", VirtualMachine.Type.User); @@ -705,6 +721,11 @@ public Long countAllocatedVMsForAccount(long accountId, boolean runningVMsonly) else sc.setParameters("state", new Object[] {State.Destroyed, State.Error, State.Expunging}); sc.setParameters("displayVm", 1); + + if (CollectionUtils.isNotEmpty(reservedResourceIds)) { + sc.setParameters("idNIN", reservedResourceIds.toArray()); + } + return customSearch(sc, null).get(0); } @@ -734,7 +755,7 @@ public List, Pair>> getVmsD while (rs.next()) { vmsDetailByNames.add(new Pair, Pair>(new Pair( rs.getString("vm_instance.instance_name"), VirtualMachine.Type.valueOf(rs.getString("vm_type"))), - new Pair(rs.getLong("vm_instance.id"), rs.getString("user_vm_details.value")))); + new Pair(rs.getLong("vm_instance.id"), rs.getString("vm_instance_details.value")))); } } } catch (SQLException e) { @@ -761,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; } @@ -792,4 +813,16 @@ public List listByIds(List ids) { sc.setParameters("ids", ids.toArray()); return listBy(sc); } + + @Override + public UserVmVO persist(UserVmVO entity) { + return Transaction.execute((TransactionCallback) status -> { + UserVmVO userVM = super.persist(entity); + reservationDao.setResourceId(Resource.ResourceType.user_vm, userVM.getId()); + reservationDao.setResourceId(Resource.ResourceType.cpu, userVM.getId()); + reservationDao.setResourceId(Resource.ResourceType.memory, userVM.getId()); + reservationDao.setResourceId(Resource.ResourceType.gpu, userVM.getId()); + return userVM; + }); + } } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDetailsDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDetailsDao.java deleted file mode 100644 index c22da6b4ff53..000000000000 --- a/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDetailsDao.java +++ /dev/null @@ -1,25 +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 com.cloud.vm.dao; - -import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; - -import com.cloud.utils.db.GenericDao; -import com.cloud.vm.UserVmDetailVO; - -public interface UserVmDetailsDao extends GenericDao, ResourceDetailsDao { -} diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDetailsDaoImpl.java deleted file mode 100644 index d8f751842d51..000000000000 --- a/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDetailsDaoImpl.java +++ /dev/null @@ -1,34 +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 com.cloud.vm.dao; - - -import org.springframework.stereotype.Component; - -import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; - -import com.cloud.vm.UserVmDetailVO; - -@Component -public class UserVmDetailsDaoImpl extends ResourceDetailsDaoBase implements UserVmDetailsDao { - - @Override - public void addDetail(long resourceId, String key, String value, boolean display) { - super.addDetail(new UserVmDetailVO(resourceId, key, value, display)); - } - -} 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 42c00231aac1..23541c2431e7 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 @@ -16,6 +16,7 @@ // under the License. package com.cloud.vm.dao; +import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -81,7 +82,7 @@ public interface VMInstanceDao extends GenericDao, StateDao< List listByHostAndState(long hostId, State... states); - List listByTypes(VirtualMachine.Type... types); + int countByTypes(VirtualMachine.Type... types); VMInstanceVO findByIdTypes(long id, VirtualMachine.Type... types); @@ -117,7 +118,7 @@ public interface VMInstanceDao extends GenericDao, StateDao< List listVmsMigratingFromHost(Long hostId); - List listByZoneWithBackups(Long zoneId, Long backupOfferingId); + List listByZoneAndBackupOffering(Long zoneId, Long backupOfferingId); public Long countActiveByHostId(long hostId); @@ -144,25 +145,51 @@ public interface VMInstanceDao extends GenericDao, StateDao< */ List listDistinctHostNames(long networkId, VirtualMachine.Type... types); + List findByHostInStatesExcluding(Long hostId, Collection excludingIds, State... states); + List findByHostInStates(Long hostId, State... states); List listStartingWithNoHostId(); boolean updatePowerState(long instanceId, long powerHostId, VirtualMachine.PowerState powerState, Date wisdomEra); + Map updatePowerState(Map instancePowerStates, + long powerHostId, Date wisdomEra); + void resetVmPowerStateTracking(long instanceId); + void resetVmPowerStateTracking(List instanceId); + void resetHostPowerStateTracking(long hostId); HashMap countVgpuVMs(Long dcId, Long podId, Long clusterId); VMInstanceVO findVMByHostNameInZone(String hostName, long zoneId); - boolean isPowerStateUpToDate(long instanceId); + boolean isPowerStateUpToDate(VMInstanceVO instance); List listNonMigratingVmsByHostEqualsLastHost(long hostId); void updateSystemVmTemplateId(long templateId, Hypervisor.HypervisorType hypervisorType); List listByHostOrLastHostOrHostPod(List hostIds, long podId); + + List searchRemovedByRemoveDate(final Date startDate, final Date endDate, final Long batchSize, + List skippedVmIds); + + Pair, Integer> listByVmsNotInClusterUsingPool(long clusterId, long poolId); + + List listIdServiceOfferingForUpVmsByHostId(Long hostId); + + List listIdServiceOfferingForVmsMigratingFromHost(Long hostId); + + Map getNameIdMapForVmInstanceNames(Collection names); + + Map getNameIdMapForVmIds(Collection ids); + + int getVmCountByOfferingId(Long serviceOfferingId); + + int getVmCountByOfferingNotInDomain(Long serviceOfferingId, List domainIds); + + List listByIdsIncludingRemoved(List ids); } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java index cc82813b412e..589a63ea0d84 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 @@ -20,25 +20,31 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import javax.annotation.PostConstruct; import javax.inject.Inject; +import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor; import com.cloud.server.ResourceTag.ResourceObjectType; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.VolumeDao; import com.cloud.tags.dao.ResourceTagDao; import com.cloud.utils.DateUtil; import com.cloud.utils.Pair; import com.cloud.utils.db.Attribute; import com.cloud.utils.db.DB; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.JoinBuilder; @@ -64,12 +70,13 @@ @Component public class VMInstanceDaoImpl extends GenericDaoBase implements VMInstanceDao { - private static final int MAX_CONSECUTIVE_SAME_STATE_UPDATE_COUNT = 3; + static final int MAX_CONSECUTIVE_SAME_STATE_UPDATE_COUNT = 3; protected SearchBuilder VMClusterSearch; protected SearchBuilder LHVMClusterSearch; protected SearchBuilder IdStatesSearch; protected SearchBuilder AllFieldsSearch; + protected SearchBuilder IdServiceOfferingIdSelectSearch; protected SearchBuilder ZoneTemplateNonExpungedSearch; protected SearchBuilder TemplateNonExpungedSearch; protected SearchBuilder NameLikeSearch; @@ -95,43 +102,59 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem protected SearchBuilder NotMigratingSearch; protected SearchBuilder BackupSearch; protected SearchBuilder LastHostAndStatesSearch; + protected SearchBuilder VmsNotInClusterUsingPool; + protected SearchBuilder IdsPowerStateSelectSearch; + GenericSearchBuilder CountByOfferingId; + GenericSearchBuilder CountUserVmNotInDomain; @Inject - ResourceTagDao _tagsDao; + ResourceTagDao tagsDao; @Inject - NicDao _nicDao; + NicDao nicDao; + @Inject + VolumeDao volumeDao; + @Inject + protected HostDao hostDao; 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 "; - private static final String COUNT_VMS_BASED_ON_VGPU_TYPES1 = + private static final String COUNT_VMS_BASED_ON_VGPU_TYPES1_LEGACY = "SELECT pci, type, SUM(vmcount) FROM (SELECT MAX(IF(offering.name = 'pciDevice',value,'')) AS pci, MAX(IF(offering.name = 'vgpuType', value,'')) " + "AS type, COUNT(DISTINCT vm.id) AS vmcount FROM service_offering_details offering INNER JOIN vm_instance vm ON offering.service_offering_id = vm.service_offering_id " + "INNER JOIN `cloud`.`host` ON vm.host_id = host.id WHERE vm.state = 'Running' AND host.data_center_id = ? "; + private static final String COUNT_VMS_BASED_ON_VGPU_TYPES2_LEGACY = + "GROUP BY vm.service_offering_id) results GROUP BY pci, type"; + + private static final String COUNT_VMS_BASED_ON_VGPU_TYPES1 = + "SELECT CONCAT(gpu_card.vendor_name, ' ', gpu_card.device_name), vgpu_profile.name, COUNT(gpu_device.vm_id) " + + "FROM `cloud`.`gpu_device` " + + "INNER JOIN `cloud`.`host` ON gpu_device.host_id = host.id " + + "INNER JOIN `cloud`.`gpu_card` ON gpu_device.card_id = gpu_card.id " + + "INNER JOIN `cloud`.`vgpu_profile` ON vgpu_profile.id = gpu_device.vgpu_profile_id " + + "WHERE vm_id IS NOT NULL AND host.data_center_id = ? "; private static final String COUNT_VMS_BASED_ON_VGPU_TYPES2 = - "GROUP BY offering.service_offering_id) results GROUP BY pci, type"; + "GROUP BY gpu_card.name, vgpu_profile.name"; private static final String UPDATE_SYSTEM_VM_TEMPLATE_ID_FOR_HYPERVISOR = "UPDATE `cloud`.`vm_instance` SET vm_template_id = ? WHERE type <> 'User' AND hypervisor_type = ? AND removed is NULL"; private static final String COUNT_VMS_BY_ZONE_AND_STATE_AND_HOST_TAG = "SELECT COUNT(1) FROM vm_instance vi JOIN service_offering so ON vi.service_offering_id=so.id " + "JOIN vm_template vt ON vi.vm_template_id = vt.id WHERE vi.data_center_id = ? AND vi.state = ? AND vi.removed IS NULL AND (so.host_tag = ? OR vt.template_tag = ?)"; - @Inject - protected HostDao _hostDao; public VMInstanceDaoImpl() { } @@ -145,13 +168,13 @@ protected void init() { IdStatesSearch.done(); VMClusterSearch = createSearchBuilder(); - SearchBuilder hostSearch = _hostDao.createSearchBuilder(); + SearchBuilder hostSearch = hostDao.createSearchBuilder(); VMClusterSearch.join("hostSearch", hostSearch, hostSearch.entity().getId(), VMClusterSearch.entity().getHostId(), JoinType.INNER); hostSearch.and("clusterId", hostSearch.entity().getClusterId(), SearchCriteria.Op.EQ); VMClusterSearch.done(); LHVMClusterSearch = createSearchBuilder(); - SearchBuilder hostSearch1 = _hostDao.createSearchBuilder(); + SearchBuilder hostSearch1 = hostDao.createSearchBuilder(); LHVMClusterSearch.join("hostSearch1", hostSearch1, hostSearch1.entity().getId(), LHVMClusterSearch.entity().getLastHostId(), JoinType.INNER); LHVMClusterSearch.and("hostid", LHVMClusterSearch.entity().getHostId(), Op.NULL); hostSearch1.and("clusterId", hostSearch1.entity().getClusterId(), SearchCriteria.Op.EQ); @@ -167,6 +190,14 @@ protected void init() { AllFieldsSearch.and("account", AllFieldsSearch.entity().getAccountId(), Op.EQ); AllFieldsSearch.done(); + IdServiceOfferingIdSelectSearch = createSearchBuilder(); + IdServiceOfferingIdSelectSearch.and("host", IdServiceOfferingIdSelectSearch.entity().getHostId(), Op.EQ); + IdServiceOfferingIdSelectSearch.and("lastHost", IdServiceOfferingIdSelectSearch.entity().getLastHostId(), Op.EQ); + IdServiceOfferingIdSelectSearch.and("state", IdServiceOfferingIdSelectSearch.entity().getState(), Op.EQ); + IdServiceOfferingIdSelectSearch.and("states", IdServiceOfferingIdSelectSearch.entity().getState(), Op.IN); + IdServiceOfferingIdSelectSearch.selectFields(IdServiceOfferingIdSelectSearch.entity().getId(), IdServiceOfferingIdSelectSearch.entity().getServiceOfferingId()); + IdServiceOfferingIdSelectSearch.done(); + ZoneTemplateNonExpungedSearch = createSearchBuilder(); ZoneTemplateNonExpungedSearch.and("zone", ZoneTemplateNonExpungedSearch.entity().getDataCenterId(), Op.EQ); ZoneTemplateNonExpungedSearch.and("template", ZoneTemplateNonExpungedSearch.entity().getTemplateId(), Op.EQ); @@ -266,6 +297,7 @@ protected void init() { HostAndStateSearch = createSearchBuilder(); HostAndStateSearch.and("host", HostAndStateSearch.entity().getHostId(), Op.EQ); HostAndStateSearch.and("states", HostAndStateSearch.entity().getState(), Op.IN); + HostAndStateSearch.and("idsNotIn", HostAndStateSearch.entity().getId(), Op.NIN); HostAndStateSearch.done(); StartingWithNoHostSearch = createSearchBuilder(); @@ -276,7 +308,7 @@ protected void init() { _updateTimeAttr = _allAttributes.get("updateTime"); assert _updateTimeAttr != null : "Couldn't get this updateTime attribute"; - SearchBuilder nicSearch = _nicDao.createSearchBuilder(); + SearchBuilder nicSearch = nicDao.createSearchBuilder(); nicSearch.and("networkId", nicSearch.entity().getNetworkId(), SearchCriteria.Op.EQ); nicSearch.and("removedNic", nicSearch.entity().getRemoved(), SearchCriteria.Op.NULL); @@ -305,6 +337,37 @@ protected void init() { LastHostAndStatesSearch.and("states", LastHostAndStatesSearch.entity().getState(), Op.IN); LastHostAndStatesSearch.done(); + VmsNotInClusterUsingPool = createSearchBuilder(); + SearchBuilder volumeSearch = volumeDao.createSearchBuilder(); + volumeSearch.and("poolId", volumeSearch.entity().getPoolId(), Op.EQ); + volumeSearch.and("removed", volumeSearch.entity().getRemoved(), Op.NULL); + VmsNotInClusterUsingPool.join("volumeSearch", volumeSearch, volumeSearch.entity().getInstanceId(), VmsNotInClusterUsingPool.entity().getId(), JoinType.INNER); + SearchBuilder hostSearch2 = hostDao.createSearchBuilder(); + hostSearch2.and("clusterId", hostSearch2.entity().getClusterId(), SearchCriteria.Op.NEQ); + VmsNotInClusterUsingPool.join("hostSearch2", hostSearch2, hostSearch2.entity().getId(), VmsNotInClusterUsingPool.entity().getHostId(), JoinType.INNER); + VmsNotInClusterUsingPool.and("vmStates", VmsNotInClusterUsingPool.entity().getState(), Op.IN); + VmsNotInClusterUsingPool.done(); + + IdsPowerStateSelectSearch = createSearchBuilder(); + IdsPowerStateSelectSearch.and("id", IdsPowerStateSelectSearch.entity().getId(), Op.IN); + IdsPowerStateSelectSearch.selectFields(IdsPowerStateSelectSearch.entity().getId(), + IdsPowerStateSelectSearch.entity().getPowerHostId(), + IdsPowerStateSelectSearch.entity().getPowerState(), + IdsPowerStateSelectSearch.entity().getPowerStateUpdateCount(), + IdsPowerStateSelectSearch.entity().getPowerStateUpdateTime()); + 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(); + } @Override @@ -440,10 +503,10 @@ public List listUpByHostId(Long hostId) { } @Override - public List listByTypes(Type... types) { + public int countByTypes(Type... types) { SearchCriteria sc = TypesSearch.create(); sc.setParameters("types", (Object[])types); - return listBy(sc); + return getCount(sc); } @Override @@ -557,13 +620,13 @@ public boolean updateState(State oldState, Event event, State newState, VirtualM logger.debug(str.toString()); } else { - logger.debug("Unable to update the vm id=" + vm.getId() + "; the vm either doesn't exist or already removed"); + logger.debug("Unable to update the vm {}; the vm either doesn't exist or already removed", vm); } } if (vo != null && vo.getState() == newState) { // allow for concurrent update if target state has already been matched - logger.debug("VM " + vo.getInstanceName() + " state has been already been updated to " + newState); + logger.debug("VM {} state has been already been updated to {}", vo, newState); return true; } } @@ -612,7 +675,7 @@ public List listVmsMigratingFromHost(Long hostId) { } @Override - public List listByZoneWithBackups(Long zoneId, Long backupOfferingId) { + public List listByZoneAndBackupOffering(Long zoneId, Long backupOfferingId) { SearchCriteria sc = BackupSearch.create(); sc.setParameters("zone_id", zoneId); if (backupOfferingId != null) { @@ -755,40 +818,55 @@ public List listHostIdsByVmCount(long dcId, Long podId, Long clusterId, lo @Override public HashMap countVgpuVMs(Long dcId, Long podId, Long clusterId) { + StringBuilder finalQueryLegacy = new StringBuilder(); StringBuilder finalQuery = new StringBuilder(); TransactionLegacy txn = TransactionLegacy.currentTxn(); + PreparedStatement pstmtLegacy = null; PreparedStatement pstmt = null; List resourceIdList = new ArrayList(); HashMap result = new HashMap(); resourceIdList.add(dcId); + finalQueryLegacy.append(COUNT_VMS_BASED_ON_VGPU_TYPES1_LEGACY); finalQuery.append(COUNT_VMS_BASED_ON_VGPU_TYPES1); if (podId != null) { + finalQueryLegacy.append("AND host.pod_id = ? "); finalQuery.append("AND host.pod_id = ? "); resourceIdList.add(podId); } if (clusterId != null) { + finalQueryLegacy.append("AND host.cluster_id = ? "); finalQuery.append("AND host.cluster_id = ? "); resourceIdList.add(clusterId); } + finalQueryLegacy.append(COUNT_VMS_BASED_ON_VGPU_TYPES2_LEGACY); finalQuery.append(COUNT_VMS_BASED_ON_VGPU_TYPES2); try { + pstmtLegacy = txn.prepareAutoCloseStatement(finalQueryLegacy.toString()); + for (int i = 0; i < resourceIdList.size(); i++) { + pstmtLegacy.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)); } - ResultSet rs = pstmt.executeQuery(); + rs = pstmt.executeQuery(); while (rs.next()) { result.put(rs.getString(1).concat(rs.getString(2)), rs.getLong(3)); } return result; } catch (SQLException e) { - throw new CloudRuntimeException("DB Exception on: " + finalQuery, e); + throw new CloudRuntimeException("DB Exception on: " + finalQueryLegacy, e); } catch (Throwable e) { - throw new CloudRuntimeException("Caught: " + finalQuery, e); + throw new CloudRuntimeException("Caught: " + finalQueryLegacy, e); } } @@ -825,7 +903,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; } @@ -834,8 +912,9 @@ public Long countByZoneAndStateAndHostTag(long dcId, State state, String hostTag public List listNonRemovedVmsByTypeAndNetwork(long networkId, VirtualMachine.Type... types) { if (NetworkTypeSearch == null) { - SearchBuilder nicSearch = _nicDao.createSearchBuilder(); + SearchBuilder nicSearch = nicDao.createSearchBuilder(); nicSearch.and("networkId", nicSearch.entity().getNetworkId(), SearchCriteria.Op.EQ); + nicSearch.and("removed", nicSearch.entity().getRemoved(), SearchCriteria.Op.NULL); NetworkTypeSearch = createSearchBuilder(); NetworkTypeSearch.and("types", NetworkTypeSearch.entity().getType(), SearchCriteria.Op.IN); @@ -871,13 +950,24 @@ public boolean remove(Long id) { txn.start(); VMInstanceVO vm = findById(id); if (vm != null && vm.getType() == Type.User) { - _tagsDao.removeByIdAndType(id, ResourceObjectType.UserVm); + tagsDao.removeByIdAndType(id, ResourceObjectType.UserVm); } boolean result = super.remove(id); txn.commit(); return result; } + @Override + public List findByHostInStatesExcluding(Long hostId, Collection excludingIds, State... states) { + SearchCriteria sc = HostAndStateSearch.create(); + sc.setParameters("host", hostId); + if (excludingIds != null && !excludingIds.isEmpty()) { + sc.setParameters("idsNotIn", excludingIds.toArray()); + } + sc.setParameters("states", (Object[])states); + return listBy(sc); + } + @Override public List findByHostInStates(Long hostId, State... states) { SearchCriteria sc = HostAndStateSearch.create(); @@ -893,46 +983,124 @@ public List listStartingWithNoHostId() { return listBy(sc); } + protected List listSelectPowerStateByIds(final List ids) { + if (CollectionUtils.isEmpty(ids)) { + return new ArrayList<>(); + } + SearchCriteria sc = IdsPowerStateSelectSearch.create(); + sc.setParameters("id", ids.toArray()); + return customSearch(sc, null); + } + + protected Integer getPowerUpdateCount(final VMInstanceVO instance, final long powerHostId, + final VirtualMachine.PowerState powerState, Date wisdomEra) { + if (instance.getPowerStateUpdateTime() == null || instance.getPowerStateUpdateTime().before(wisdomEra)) { + Long savedPowerHostId = instance.getPowerHostId(); + boolean isStateMismatch = instance.getPowerState() != powerState + || savedPowerHostId == null + || !savedPowerHostId.equals(powerHostId) + || !isPowerStateInSyncWithInstanceState(powerState, powerHostId, instance); + if (isStateMismatch) { + return 1; + } else if (instance.getPowerStateUpdateCount() < MAX_CONSECUTIVE_SAME_STATE_UPDATE_COUNT) { + return instance.getPowerStateUpdateCount() + 1; + } + } + return null; + } + @Override - public boolean updatePowerState(final long instanceId, final long powerHostId, final VirtualMachine.PowerState powerState, Date wisdomEra) { - return Transaction.execute(new TransactionCallback() { - @Override - public Boolean doInTransaction(TransactionStatus status) { - boolean needToUpdate = false; - VMInstanceVO instance = findById(instanceId); - if (instance != null - && (null == instance.getPowerStateUpdateTime() - || instance.getPowerStateUpdateTime().before(wisdomEra))) { - Long savedPowerHostId = instance.getPowerHostId(); - if (instance.getPowerState() != powerState || savedPowerHostId == null - || savedPowerHostId.longValue() != powerHostId) { - instance.setPowerState(powerState); - instance.setPowerHostId(powerHostId); - instance.setPowerStateUpdateCount(1); - instance.setPowerStateUpdateTime(DateUtil.currentGMTTime()); - needToUpdate = true; - update(instanceId, instance); - } else { - // to reduce DB updates, consecutive same state update for more than 3 times - if (instance.getPowerStateUpdateCount() < MAX_CONSECUTIVE_SAME_STATE_UPDATE_COUNT) { - instance.setPowerStateUpdateCount(instance.getPowerStateUpdateCount() + 1); - instance.setPowerStateUpdateTime(DateUtil.currentGMTTime()); - needToUpdate = true; - update(instanceId, instance); - } - } + public boolean updatePowerState(final long instanceId, final long powerHostId, + final VirtualMachine.PowerState powerState, Date wisdomEra) { + return Transaction.execute((TransactionCallback) status -> { + VMInstanceVO instance = findById(instanceId); + if (instance == null) { + return false; + } + // Check if we need to update based on powerStateUpdateTime + if (instance.getPowerStateUpdateTime() == null || instance.getPowerStateUpdateTime().before(wisdomEra)) { + Long savedPowerHostId = instance.getPowerHostId(); + boolean isStateMismatch = instance.getPowerState() != powerState + || savedPowerHostId == null + || !savedPowerHostId.equals(powerHostId) + || !isPowerStateInSyncWithInstanceState(powerState, powerHostId, instance); + + if (isStateMismatch) { + instance.setPowerState(powerState); + instance.setPowerHostId(powerHostId); + instance.setPowerStateUpdateCount(1); + } else if (instance.getPowerStateUpdateCount() < MAX_CONSECUTIVE_SAME_STATE_UPDATE_COUNT) { + instance.setPowerStateUpdateCount(instance.getPowerStateUpdateCount() + 1); + } else { + // No need to update if power state is already in sync and count exceeded + return false; } - return needToUpdate; + instance.setPowerStateUpdateTime(DateUtil.currentGMTTime()); + update(instanceId, instance); + return true; // Return true since an update occurred } + return false; }); } @Override - public boolean isPowerStateUpToDate(final long instanceId) { - VMInstanceVO instance = findById(instanceId); - if(instance == null) { - throw new CloudRuntimeException("checking power state update count on non existing instance " + instanceId); + public Map updatePowerState( + final Map instancePowerStates, long powerHostId, Date wisdomEra) { + Map notUpdated = new HashMap<>(); + List instances = listSelectPowerStateByIds(new ArrayList<>(instancePowerStates.keySet())); + Map updateCounts = new HashMap<>(); + for (VMInstanceVO instance : instances) { + VirtualMachine.PowerState powerState = instancePowerStates.get(instance.getId()); + Integer count = getPowerUpdateCount(instance, powerHostId, powerState, wisdomEra); + if (count != null) { + updateCounts.put(instance.getId(), count); + } else { + notUpdated.put(instance.getId(), powerState); + } + } + if (updateCounts.isEmpty()) { + return notUpdated; } + StringBuilder sql = new StringBuilder("UPDATE `cloud`.`vm_instance` SET " + + "`power_host` = ?, `power_state_update_time` = now(), `power_state` = CASE "); + updateCounts.keySet().forEach(key -> { + sql.append("WHEN id = ").append(key).append(" THEN '").append(instancePowerStates.get(key)).append("' "); + }); + sql.append("END, `power_state_update_count` = CASE "); + StringBuilder idList = new StringBuilder(); + updateCounts.forEach((key, value) -> { + sql.append("WHEN `id` = ").append(key).append(" THEN ").append(value).append(" "); + idList.append(key).append(","); + }); + idList.setLength(idList.length() - 1); + sql.append("END WHERE `id` IN (").append(idList).append(")"); + TransactionLegacy txn = TransactionLegacy.currentTxn(); + try (PreparedStatement pstmt = txn.prepareAutoCloseStatement(sql.toString())) { + pstmt.setLong(1, powerHostId); + pstmt.executeUpdate(); + } catch (SQLException e) { + logger.error("Unable to execute update power states SQL from VMs {} due to: {}", + idList, e.getMessage(), e); + return instancePowerStates; + } + return notUpdated; + } + + private boolean isPowerStateInSyncWithInstanceState(final VirtualMachine.PowerState powerState, final long powerHostId, final VMInstanceVO instance) { + State instanceState = instance.getState(); + 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); + logger.debug("VM: {} on host: {} and power host : {} is in {} state, but power state is {}", + instance, instanceHost, powerHost, instanceState, powerState); + return false; + } + return true; + } + + @Override + public boolean isPowerStateUpToDate(final VMInstanceVO instance) { return instance.getPowerStateUpdateCount() < MAX_CONSECUTIVE_SAME_STATE_UPDATE_COUNT; } @@ -951,6 +1119,25 @@ public void doInTransactionWithoutResult(TransactionStatus status) { }); } + @Override + public void resetVmPowerStateTracking(List instanceIds) { + if (CollectionUtils.isEmpty(instanceIds)) { + return; + } + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + SearchCriteria sc = IdsPowerStateSelectSearch.create(); + sc.setParameters("id", instanceIds.toArray()); + VMInstanceVO vm = createForUpdate(); + vm.setPowerStateUpdateCount(0); + vm.setPowerStateUpdateTime(DateUtil.currentGMTTime()); + UpdateBuilder ub = getUpdateBuilder(vm); + update(ub, sc, null); + } + }); + } + @Override @DB public void resetHostPowerStateTracking(final long hostId) { Transaction.execute(new TransactionCallbackNoReturn() { @@ -1003,4 +1190,110 @@ public List listByHostOrLastHostOrHostPod(List hostIds, long sc.setParameters("podId", String.valueOf(podId)); return listBy(sc); } + + @Override + public List searchRemovedByRemoveDate(Date startDate, Date endDate, Long batchSize, + List skippedVmIds) { + SearchBuilder sb = createSearchBuilder(); + sb.and("removed", sb.entity().getRemoved(), SearchCriteria.Op.NNULL); + sb.and("startDate", sb.entity().getRemoved(), SearchCriteria.Op.GTEQ); + sb.and("endDate", sb.entity().getRemoved(), SearchCriteria.Op.LTEQ); + sb.and("skippedVmIds", sb.entity().getId(), Op.NOTIN); + SearchCriteria sc = sb.create(); + if (startDate != null) { + sc.setParameters("startDate", startDate); + } + if (endDate != null) { + sc.setParameters("endDate", endDate); + } + if (CollectionUtils.isNotEmpty(skippedVmIds)) { + sc.setParameters("skippedVmIds", skippedVmIds.toArray()); + } + Filter filter = new Filter(VMInstanceVO.class, "id", true, 0L, batchSize); + return searchIncludingRemoved(sc, filter, null, false); + } + + @Override + public Pair, Integer> listByVmsNotInClusterUsingPool(long clusterId, long poolId) { + SearchCriteria sc = VmsNotInClusterUsingPool.create(); + sc.setParameters("vmStates", State.Starting, State.Running, State.Stopping, State.Migrating, State.Restoring); + sc.setJoinParameters("volumeSearch", "poolId", poolId); + sc.setJoinParameters("hostSearch2", "clusterId", clusterId); + List vms = search(sc, null); + List uniqueVms = vms.stream().distinct().collect(Collectors.toList()); + return new Pair<>(uniqueVms, uniqueVms.size()); + } + + @Override + public List listIdServiceOfferingForUpVmsByHostId(Long hostId) { + SearchCriteria sc = IdServiceOfferingIdSelectSearch.create(); + sc.setParameters("host", hostId); + sc.setParameters("states", new Object[] {State.Starting, State.Running, State.Stopping, State.Migrating}); + return customSearch(sc, null); + } + + @Override + public List listIdServiceOfferingForVmsMigratingFromHost(Long hostId) { + SearchCriteria sc = IdServiceOfferingIdSelectSearch.create(); + sc.setParameters("lastHost", hostId); + sc.setParameters("state", State.Migrating); + return customSearch(sc, null); + } + + @Override + public Map getNameIdMapForVmInstanceNames(Collection names) { + SearchBuilder sb = createSearchBuilder(); + sb.and("name", sb.entity().getInstanceName(), Op.IN); + sb.selectFields(sb.entity().getId(), sb.entity().getInstanceName()); + SearchCriteria sc = sb.create(); + sc.setParameters("name", names.toArray()); + List vms = customSearch(sc, null); + return vms.stream() + .collect(Collectors.toMap(VMInstanceVO::getInstanceName, VMInstanceVO::getId)); + } + + @Override + public Map getNameIdMapForVmIds(Collection ids) { + SearchBuilder sb = createSearchBuilder(); + sb.and("id", sb.entity().getId(), Op.IN); + sb.selectFields(sb.entity().getId(), sb.entity().getInstanceName()); + SearchCriteria sc = sb.create(); + sc.setParameters("id", ids.toArray()); + List vms = customSearch(sc, null); + return vms.stream() + .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(); + idsSearch.and("ids", idsSearch.entity().getId(), SearchCriteria.Op.IN); + idsSearch.done(); + SearchCriteria sc = idsSearch.create(); + sc.setParameters("ids", ids.toArray()); + return listIncludingRemovedBy(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDetailsDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDetailsDao.java new file mode 100644 index 000000000000..4cbdc516ba0b --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDetailsDao.java @@ -0,0 +1,26 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.vm.dao; + +import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; + +import com.cloud.utils.db.GenericDao; +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 new file mode 100644 index 000000000000..4c2fdd6f8d45 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDetailsDaoImpl.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.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 +public class VMInstanceDetailsDaoImpl extends ResourceDetailsDaoBase implements VMInstanceDetailsDao { + + @Override + 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/VmStatsDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/VmStatsDao.java index 879faaf5c901..0d7aa703a8cb 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/VmStatsDao.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/VmStatsDao.java @@ -75,8 +75,10 @@ public interface VmStatsDao extends GenericDao { /** * Removes (expunges) all VM stats with {@code timestamp} less than * a given Date. - * @param limit the maximum date to keep stored. Records that exceed this limit will be removed. + * @param limitDate the maximum date to keep stored. Records that exceed this limit will be removed. + * @param limitPerQuery the maximum amount of rows to be removed in a single query. We loop if there are still rows to be removed after a given query. + * If 0 or negative, no limit is used. */ - void removeAllByTimestampLessThan(Date limit); + void removeAllByTimestampLessThan(Date limitDate, long limitPerQuery); } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/VmStatsDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/VmStatsDaoImpl.java index 1bef8f0626c3..327acec0c179 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/VmStatsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/VmStatsDaoImpl.java @@ -21,6 +21,8 @@ import javax.annotation.PostConstruct; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.stereotype.Component; import com.cloud.utils.db.Filter; @@ -33,6 +35,8 @@ @Component public class VmStatsDaoImpl extends GenericDaoBase implements VmStatsDao { + protected Logger logger = LogManager.getLogger(getClass()); + protected SearchBuilder vmIdSearch; protected SearchBuilder vmIdTimestampGreaterThanEqualSearch; protected SearchBuilder vmIdTimestampLessThanEqualSearch; @@ -113,10 +117,15 @@ public void removeAllByVmId(long vmId) { } @Override - public void removeAllByTimestampLessThan(Date limit) { + public void removeAllByTimestampLessThan(Date limitDate, long limitPerQuery) { SearchCriteria sc = timestampSearch.create(); - sc.setParameters("timestamp", limit); - expunge(sc); + sc.setParameters("timestamp", limitDate); + + logger.debug(String.format("Starting to remove all vm_stats rows older than [%s].", limitDate)); + + long totalRemoved = batchExpunge(sc, limitPerQuery); + + logger.info(String.format("Removed a total of [%s] vm_stats rows older than [%s].", totalRemoved, limitDate)); } } diff --git a/engine/schema/src/main/java/com/cloud/vm/snapshot/VMSnapshotVO.java b/engine/schema/src/main/java/com/cloud/vm/snapshot/VMSnapshotVO.java index c48396ad0219..5b6f97b82e70 100644 --- a/engine/schema/src/main/java/com/cloud/vm/snapshot/VMSnapshotVO.java +++ b/engine/schema/src/main/java/com/cloud/vm/snapshot/VMSnapshotVO.java @@ -36,6 +36,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.VMSnapshotOptions; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "vm_snapshots") @@ -145,6 +146,13 @@ public VMSnapshotVO(Long accountId, Long domainId, Long vmId, String description this.serviceOfferingId = serviceOfferingId; } + @Override + public String toString() { + return String.format("VMSnapshot %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "vmId")); + } + @Override public String getDescription() { return description; 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 31999ef15d66..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 @@ -27,6 +27,8 @@ public interface VMSnapshotDao extends GenericDao, StateDao< List findByVm(Long vmId); + List findByVmAndByType(Long vmId, VMSnapshot.Type type); + List listExpungingSnapshot(); List listByInstanceId(Long vmId, VMSnapshot.State... status); @@ -35,7 +37,13 @@ public interface VMSnapshotDao extends GenericDao, StateDao< List listByParent(Long vmSnapshotId); + List listByParentAndStateIn(Long vmSnapshotId, VMSnapshot.State... states); + VMSnapshotVO findByName(Long vmId, String name); List listByAccountId(Long accountId); + + 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 062960130aca..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 @@ -17,12 +17,14 @@ package com.cloud.vm.snapshot.dao; +import java.util.ArrayList; import java.util.Date; import java.util.List; - +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; @@ -40,6 +42,12 @@ public class VMSnapshotDaoImpl extends GenericDaoBase implem private final SearchBuilder SnapshotStatusSearch; private final SearchBuilder AllFieldsSearch; + private SearchBuilder parentIdEqAndStateIn; + + private static final String PARENT = "parent"; + + private static final String STATE = "state"; + protected VMSnapshotDaoImpl() { AllFieldsSearch = createSearchBuilder(); AllFieldsSearch.and("state", AllFieldsSearch.entity().getState(), Op.EQ); @@ -69,6 +77,11 @@ protected VMSnapshotDaoImpl() { SnapshotStatusSearch.and("vm_id", SnapshotStatusSearch.entity().getVmId(), SearchCriteria.Op.EQ); SnapshotStatusSearch.and("state", SnapshotStatusSearch.entity().getState(), SearchCriteria.Op.IN); SnapshotStatusSearch.done(); + + parentIdEqAndStateIn = createSearchBuilder(); + parentIdEqAndStateIn.and(PARENT, parentIdEqAndStateIn.entity().getParent(), Op.EQ); + parentIdEqAndStateIn.and(STATE, parentIdEqAndStateIn.entity().getState(), Op.IN); + parentIdEqAndStateIn.done(); } @Override @@ -78,6 +91,14 @@ public List findByVm(Long vmId) { return listBy(sc, null); } + @Override + public List findByVmAndByType(Long vmId, VMSnapshot.Type type) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("vm_id", vmId); + sc.setParameters("vm_snapshot_type", type); + return listBy(sc, null); + } + @Override public List listExpungingSnapshot() { SearchCriteria sc = ExpungingSnapshotSearch.create(); @@ -109,6 +130,14 @@ public List listByParent(Long vmSnapshotId) { return listBy(sc, null); } + @Override + public List listByParentAndStateIn(Long vmSnapshotId, State... states) { + SearchCriteria sc = parentIdEqAndStateIn.create(); + sc.setParameters(PARENT, vmSnapshotId); + sc.setParameters(STATE, (Object[])states); + return listBy(sc, null); + } + @Override public VMSnapshotVO findByName(Long vmId, String name) { SearchCriteria sc = AllFieldsSearch.create(); @@ -174,10 +203,35 @@ public boolean updateState(State currentState, Event event, State nextState, VMS .append("; updatedTime=") .append(oldUpdatedTime); } else { - logger.debug("Unable to update VM snapshot: id=" + vo.getId() + ", as there is no such snapshot exists in the database anymore"); + logger.debug("Unable to update Instance Snapshot: {}, as there is no such Snapshot exists in the database anymore", vo); } } return rows > 0; } + @Override + public List searchByVms(List vmIds) { + if (CollectionUtils.isEmpty(vmIds)) { + return new ArrayList<>(); + } + SearchBuilder sb = createSearchBuilder(); + sb.and("vmIds", sb.entity().getVmId(), SearchCriteria.Op.IN); + SearchCriteria sc = sb.create(); + sc.setParameters("vmIds", vmIds.toArray()); + return search(sc, null); + } + + @Override + public List searchRemovedByVms(List vmIds, Long batchSize) { + if (CollectionUtils.isEmpty(vmIds)) { + return new ArrayList<>(); + } + SearchBuilder sb = createSearchBuilder(); + sb.and("vmIds", sb.entity().getVmId(), SearchCriteria.Op.IN); + sb.and("removed", sb.entity().getRemoved(), SearchCriteria.Op.NNULL); + SearchCriteria sc = sb.create(); + sc.setParameters("vmIds", vmIds.toArray()); + Filter filter = new Filter(VMSnapshotVO.class, "id", true, 0L, batchSize); + return searchIncludingRemoved(sc, filter, null, false); + } } 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..eb38b08f6151 --- /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") + 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/RoleVO.java b/engine/schema/src/main/java/org/apache/cloudstack/acl/RoleVO.java index d4647255fc67..cff139a9263a 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/acl/RoleVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/acl/RoleVO.java @@ -58,11 +58,16 @@ public class RoleVO implements Role { @Column(name = "public_role") private boolean publicRole = true; + @Column(name = "state") + @Enumerated(value = EnumType.STRING) + private State state; + @Column(name = GenericDao.REMOVED_COLUMN) private Date removed; public RoleVO() { this.uuid = UUID.randomUUID().toString(); + this.state = State.ENABLED; } public RoleVO(final String name, final RoleType roleType, final String description) { @@ -121,7 +126,8 @@ public boolean isDefault() { @Override public String toString() { - return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "name", "uuid", "roleType"); + return String.format("Role %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "roleType")); } public boolean isPublicRole() { @@ -131,4 +137,12 @@ public boolean isPublicRole() { public void setPublicRole(boolean publicRole) { this.publicRole = publicRole; } + + public State getState() { + return state; + } + + public void setState(State state) { + this.state = state; + } } 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/acl/dao/RoleDao.java b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDao.java index a776f7b6fe66..f4fdb6a2b161 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDao.java @@ -28,13 +28,15 @@ public interface RoleDao extends GenericDao { List findAllByName(String roleName, boolean showPrivateRole); - Pair, Integer> findAllByName(final String roleName, String keyword, Long offset, Long limit, boolean showPrivateRole); + Pair, Integer> findAllByName(final String roleName, String keyword, String state, Long offset, Long limit, boolean showPrivateRole); List findAllByRoleType(RoleType type, boolean showPrivateRole); List findByName(String roleName, boolean showPrivateRole); RoleVO findByNameAndType(String roleName, RoleType type, boolean showPrivateRole); - Pair, Integer> findAllByRoleType(RoleType type, Long offset, Long limit, boolean showPrivateRole); + Pair, Integer> findAllByRoleType(RoleType type, String state, Long offset, Long limit, boolean showPrivateRole); - Pair, Integer> listAllRoles(Long startIndex, Long limit, boolean showPrivateRole); + Pair, Integer> listAllRoles(String state, Long startIndex, Long limit, boolean showPrivateRole); + + List searchByIds(Long... ids); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDaoImpl.java index 06d3108076aa..48c0d828a415 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/acl/dao/RoleDaoImpl.java @@ -28,10 +28,13 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; +import java.util.Collections; import java.util.List; @Component public class RoleDaoImpl extends GenericDaoBase implements RoleDao { + + private final SearchBuilder RoleByIdsSearch; private final SearchBuilder RoleByNameSearch; private final SearchBuilder RoleByTypeSearch; private final SearchBuilder RoleByNameAndTypeSearch; @@ -40,14 +43,20 @@ public class RoleDaoImpl extends GenericDaoBase implements RoleDao public RoleDaoImpl() { super(); + RoleByIdsSearch = createSearchBuilder(); + RoleByIdsSearch.and("idIN", RoleByIdsSearch.entity().getId(), SearchCriteria.Op.IN); + RoleByIdsSearch.done(); + RoleByNameSearch = createSearchBuilder(); RoleByNameSearch.and("roleName", RoleByNameSearch.entity().getName(), SearchCriteria.Op.LIKE); RoleByNameSearch.and("isPublicRole", RoleByNameSearch.entity().isPublicRole(), SearchCriteria.Op.EQ); + RoleByNameSearch.and("state", RoleByNameSearch.entity().getState(), SearchCriteria.Op.EQ); RoleByNameSearch.done(); RoleByTypeSearch = createSearchBuilder(); RoleByTypeSearch.and("roleType", RoleByTypeSearch.entity().getRoleType(), SearchCriteria.Op.EQ); RoleByTypeSearch.and("isPublicRole", RoleByTypeSearch.entity().isPublicRole(), SearchCriteria.Op.EQ); + RoleByTypeSearch.and("state", RoleByTypeSearch.entity().getState(), SearchCriteria.Op.EQ); RoleByTypeSearch.done(); RoleByNameAndTypeSearch = createSearchBuilder(); @@ -58,16 +67,17 @@ public RoleDaoImpl() { RoleByIsPublicSearch = createSearchBuilder(); RoleByIsPublicSearch.and("isPublicRole", RoleByIsPublicSearch.entity().isPublicRole(), SearchCriteria.Op.EQ); + RoleByIsPublicSearch.and("state", RoleByIsPublicSearch.entity().getState(), SearchCriteria.Op.EQ); RoleByIsPublicSearch.done(); } @Override public List findAllByName(final String roleName, boolean showPrivateRole) { - return findAllByName(roleName, null, null, null, showPrivateRole).first(); + return findAllByName(roleName, null, null, null, null, showPrivateRole).first(); } @Override - public Pair, Integer> findAllByName(final String roleName, String keyword, Long offset, Long limit, boolean showPrivateRole) { + public Pair, Integer> findAllByName(final String roleName, String keyword, String state, Long offset, Long limit, boolean showPrivateRole) { SearchCriteria sc = RoleByNameSearch.create(); filterPrivateRolesIfNeeded(sc, showPrivateRole); if (StringUtils.isNotEmpty(roleName)) { @@ -76,19 +86,25 @@ public Pair, Integer> findAllByName(final String roleName, String k if (StringUtils.isNotEmpty(keyword)) { sc.setParameters("roleName", "%" + keyword + "%"); } + if (StringUtils.isNotEmpty(state)) { + sc.setParameters("state", state); + } return searchAndCount(sc, new Filter(RoleVO.class, "id", true, offset, limit)); } @Override public List findAllByRoleType(final RoleType type, boolean showPrivateRole) { - return findAllByRoleType(type, null, null, showPrivateRole).first(); + return findAllByRoleType(type, null, null, null, showPrivateRole).first(); } - public Pair, Integer> findAllByRoleType(final RoleType type, Long offset, Long limit, boolean showPrivateRole) { + public Pair, Integer> findAllByRoleType(final RoleType type, String state, Long offset, Long limit, boolean showPrivateRole) { SearchCriteria sc = RoleByTypeSearch.create(); filterPrivateRolesIfNeeded(sc, showPrivateRole); sc.setParameters("roleType", type); + if (StringUtils.isNotEmpty(state)) { + sc.setParameters("state", state); + } return searchAndCount(sc, new Filter(RoleVO.class, "id", true, offset, limit)); } @@ -110,12 +126,25 @@ public RoleVO findByNameAndType(String roleName, RoleType type, boolean showPriv } @Override - public Pair, Integer> listAllRoles(Long startIndex, Long limit, boolean showPrivateRole) { + public Pair, Integer> listAllRoles(String state, Long startIndex, Long limit, boolean showPrivateRole) { SearchCriteria sc = RoleByIsPublicSearch.create(); + if (StringUtils.isNotEmpty(state)) { + sc.setParameters("state", state); + } filterPrivateRolesIfNeeded(sc, showPrivateRole); return searchAndCount(sc, new Filter(RoleVO.class, "id", true, startIndex, limit)); } + @Override + public List searchByIds(Long... ids) { + if (ids == null || ids.length == 0) { + return Collections.emptyList(); + } + SearchCriteria sc = RoleByIdsSearch.create(); + sc.setParameters("idIN", ids); + return listBy(sc); + } + public void filterPrivateRolesIfNeeded(SearchCriteria sc, boolean showPrivateRole) { if (!showPrivateRole) { sc.setParameters("isPublicRole", true); diff --git a/engine/schema/src/main/java/org/apache/cloudstack/affinity/AffinityGroupVO.java b/engine/schema/src/main/java/org/apache/cloudstack/affinity/AffinityGroupVO.java index 536b96c6567e..9b8fc5981719 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/affinity/AffinityGroupVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/affinity/AffinityGroupVO.java @@ -28,6 +28,7 @@ import javax.persistence.Table; import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "affinity_group") @@ -119,9 +120,8 @@ public ControlledEntity.ACLType getAclType() { @Override public String toString() { - StringBuilder buf = new StringBuilder("AffinityGroup["); - buf.append(uuid).append("]"); - return buf.toString(); + return String.format("AffinityGroup %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name")); } @Override diff --git a/engine/schema/src/main/java/org/apache/cloudstack/affinity/dao/AffinityGroupDao.java b/engine/schema/src/main/java/org/apache/cloudstack/affinity/dao/AffinityGroupDao.java index 010720ba33aa..859b29215044 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/affinity/dao/AffinityGroupDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/affinity/dao/AffinityGroupDao.java @@ -38,4 +38,7 @@ public interface AffinityGroupDao extends GenericDao { AffinityGroupVO findByAccountAndType(Long accountId, String string); AffinityGroupVO findDomainLevelGroupByType(Long domainId, String string); + + List listByIds(List ids, boolean exclusive); + } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/affinity/dao/AffinityGroupDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/affinity/dao/AffinityGroupDaoImpl.java index 3bd7c6d082b2..5bd598f36a0f 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/affinity/dao/AffinityGroupDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/affinity/dao/AffinityGroupDaoImpl.java @@ -31,6 +31,7 @@ import com.cloud.utils.db.SearchCriteria; public class AffinityGroupDaoImpl extends GenericDaoBase implements AffinityGroupDao { + private SearchBuilder IdsSearch; private SearchBuilder AccountIdSearch; private SearchBuilder AccountIdNameSearch; private SearchBuilder AccountIdNamesSearch; @@ -47,6 +48,10 @@ public AffinityGroupDaoImpl() { @PostConstruct protected void init() { + IdsSearch = createSearchBuilder(); + IdsSearch.and("idIn", IdsSearch.entity().getId(), SearchCriteria.Op.IN); + IdsSearch.done(); + AccountIdSearch = createSearchBuilder(); AccountIdSearch.and("accountId", AccountIdSearch.entity().getAccountId(), SearchCriteria.Op.EQ); AccountIdSearch.done(); @@ -158,4 +163,11 @@ public AffinityGroupVO findDomainLevelGroupByType(Long domainId, String type) { sc.setJoinParameters("domainTypeSearch", "domainId", domainId); return findOneBy(sc); } + + @Override + public List listByIds(List ids, boolean exclusive) { + SearchCriteria sc = IdsSearch.create(); + sc.setParameters("idIn", ids.toArray()); + return lockRows(sc, null, exclusive); + } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupDetailVO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupDetailVO.java new file mode 100644 index 000000000000..aaf63518708c --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupDetailVO.java @@ -0,0 +1,98 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.backup; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.cloudstack.api.ResourceDetail; + +@Entity +@Table(name = "backup_details") +public class BackupDetailVO implements ResourceDetail { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "backup_id") + private long resourceId; + + @Column(name = "name") + private String name; + + @Column(name = "value", length = 65536) + private String value; + + @Column(name = "display") + private boolean display = true; + + public BackupDetailVO() { + } + + public BackupDetailVO(long backupId, String name, String value, boolean display) { + this.resourceId = backupId; + this.name = name; + this.value = value; + this.display = display; + } + + @Override + public long getId() { + return id; + } + + @Override + public long getResourceId() { + return resourceId; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getValue() { + return value; + } + + @Override + public boolean isDisplay() { + return display; + } + + public void setId(long id) { + this.id = id; + } + + public void setResourceId(long resourceId) { + this.resourceId = resourceId; + } + + public void setName(String name) { + this.name = name; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/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..ebeb7d4a2d59 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; @@ -131,4 +133,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 new file mode 100644 index 000000000000..1764496a6c0b --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupRepositoryVO.java @@ -0,0 +1,184 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.backup; + +import com.cloud.utils.db.Encrypt; + +import java.util.Date; +import java.util.UUID; + +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; + +@Entity +@Table(name = "backup_repository") +public class BackupRepositoryVO implements BackupRepository { + @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 = "zone_id", nullable = false) + private long zoneId; + + @Column(name = "provider", nullable = false) + private String provider; + + @Column(name = "type", nullable = false) + private String type; + + @Column(name = "address", nullable = false) + private String address; + + @Encrypt + @Column(name = "mount_opts") + private String mountOptions; + + @Column(name = "used_bytes",nullable = true) + private Long usedBytes; + + @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; + + @Column(name = "removed") + @Temporal(value = TemporalType.TIMESTAMP) + private Date removed; + + 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, final Boolean crossZoneInstanceCreation) { + this(); + this.zoneId = zoneId; + this.provider = provider; + this.name = name; + this.type = type; + this.address = address; + this.mountOptions = mountOptions; + this.capacityBytes = capacityBytes; + this.crossZoneInstanceCreation = crossZoneInstanceCreation; + this.created = new Date(); + } + + public String getUuid() { + return uuid; + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public long getZoneId() { + return zoneId; + } + + @Override + public String getProvider() { + return provider; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public void setAddress(String address) { + this.address = address; + } + + @Override + public String getAddress() { + return address; + } + + @Override + public String getMountOptions() { + return mountOptions; + } + + @Override + public void setMountOptions(String mountOptions) { + this.mountOptions = mountOptions; + } + + @Override + public Long getUsedBytes() { + return usedBytes; + } + + @Override + public void setUsedBytes(Long usedBytes) { + this.usedBytes = usedBytes; + } + + @Override + 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; + } + + public Date getCreated() { + return created; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupScheduleVO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupScheduleVO.java index ba31dc59d390..1ee2cff78b65 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupScheduleVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupScheduleVO.java @@ -18,6 +18,7 @@ package org.apache.cloudstack.backup; import java.util.Date; +import java.util.UUID; import javax.persistence.Column; import javax.persistence.Entity; @@ -29,6 +30,7 @@ import javax.persistence.TemporalType; import com.cloud.utils.DateUtil; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "backup_schedule") @@ -38,6 +40,9 @@ public class BackupScheduleVO implements BackupSchedule { @Column(name = "id") private long id; + @Column(name = "uuid", nullable = false) + private String uuid = UUID.randomUUID().toString(); + @Column(name = "vm_id") private Long vmId; @@ -57,15 +62,37 @@ public class BackupScheduleVO implements BackupSchedule { @Column(name = "async_job_id") Long asyncJobId; + @Column(name = "max_backups") + private int maxBackups = 0; + + @Column(name = "quiescevm") + Boolean quiesceVM = false; + + @Column(name = "account_id") + Long accountId; + + @Column(name = "domain_id") + Long domainId; + public BackupScheduleVO() { } - public BackupScheduleVO(Long vmId, DateUtil.IntervalType scheduleType, String schedule, String timezone, Date scheduledTimestamp) { + public BackupScheduleVO(Long vmId, DateUtil.IntervalType scheduleType, String schedule, String timezone, Date scheduledTimestamp, int maxBackups, Boolean quiesceVM, Long accountId, Long domainId) { this.vmId = vmId; this.scheduleType = (short) scheduleType.ordinal(); this.schedule = schedule; this.timezone = timezone; this.scheduledTimestamp = scheduledTimestamp; + this.maxBackups = maxBackups; + this.quiesceVM = quiesceVM; + this.accountId = accountId; + this.domainId = domainId; + } + + @Override + public String toString() { + return String.format("BackupSchedule %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "vmId", "schedule", "scheduleType")); } @Override @@ -73,6 +100,11 @@ public long getId() { return id; } + @Override + public String getUuid() { + return uuid; + } + public Long getVmId() { return vmId; } @@ -121,4 +153,48 @@ public Long getAsyncJobId() { public void setAsyncJobId(Long asyncJobId) { this.asyncJobId = asyncJobId; } + + public int getMaxBackups() { + return maxBackups; + } + + public void setMaxBackups(int maxBackups) { + this.maxBackups = maxBackups; + } + + public void setQuiesceVM(Boolean quiesceVM) { + this.quiesceVM = 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; + } } 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 3e5db0443d8c..0f8a10fb7be6 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 @@ -18,8 +18,16 @@ package org.apache.cloudstack.backup; import com.cloud.utils.db.GenericDao; +import com.google.gson.Gson; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; +import java.util.Collections; import java.util.Date; +import java.util.List; +import java.util.Map; import java.util.UUID; import javax.persistence.Column; @@ -32,6 +40,7 @@ import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; +import javax.persistence.Transient; @Entity @Table(name = "backups") @@ -41,11 +50,17 @@ public class BackupVO implements Backup { @Column(name = "id") private long id; + @Column(name = "name") + private String name; + + @Column(name = "description") + private String description; + @Column(name = "uuid") private String uuid; @Column(name = "vm_id") - private long vmId; + private Long vmId; @Column(name = "external_id") private String externalId; @@ -82,10 +97,25 @@ public class BackupVO implements Backup { @Column(name = "zone_id") private long zoneId; + @Column(name = "backed_volumes", length = 65535) + protected String backedUpVolumes; + + @Column(name = "backup_schedule_id") + private Long backupScheduleId; + + @Transient + Map details; + public BackupVO() { this.uuid = UUID.randomUUID().toString(); } + @Override + public String toString() { + return String.format("Backup %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "vmId", "backupType", "externalId")); + } + @Override public long getId() { return id; @@ -97,11 +127,11 @@ public String getUuid() { } @Override - public long getVmId() { + public Long getVmId() { return vmId; } - public void setVmId(long vmId) { + public void setVmId(Long vmId) { this.vmId = vmId; } @@ -158,6 +188,7 @@ public void setStatus(Status status) { this.status = status; } + @Override public long getBackupOfferingId() { return backupOfferingId; } @@ -199,14 +230,62 @@ public Class getEntityType() { @Override public String getName() { - return null; + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(String description) { + this.description = description; + } + + public List getBackedUpVolumes() { + if (StringUtils.isEmpty(this.backedUpVolumes)) { + return Collections.emptyList(); + } + return Arrays.asList(new Gson().fromJson(this.backedUpVolumes, Backup.VolumeInfo[].class)); + } + + public void setBackedUpVolumes(String backedUpVolumes) { + this.backedUpVolumes = backedUpVolumes; + } + + @Override + public Map getDetails() { + return details; + } + + @Override + public String getDetail(String name) { + return this.details.get(name); + } + + public void setDetails(Map details) { + this.details = details; } public Date getRemoved() { return removed; } - public void setRemoved(Date removed) { this.removed = removed; } + + @Override + public Long getBackupScheduleId() { + return backupScheduleId; + } + + public void setBackupScheduleId(Long backupScheduleId) { + this.backupScheduleId = backupScheduleId; + } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDao.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDao.java index 5d2f5ac64d61..e60e49e1a0c2 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDao.java @@ -19,7 +19,6 @@ import java.util.List; -import org.apache.cloudstack.api.response.BackupResponse; import org.apache.cloudstack.backup.Backup; import org.apache.cloudstack.backup.BackupVO; @@ -32,9 +31,15 @@ public interface BackupDao extends GenericDao { List listByVmId(Long zoneId, Long vmId); List listByAccountId(Long accountId); - List listByOfferingId(Long offeringId); List syncBackups(Long zoneId, Long vmId, List externalBackups); + List listByVmIdAndOffering(Long zoneId, Long vmId, Long offeringId); + List searchByVmIds(List vmIds); BackupVO getBackupVO(Backup backup); - - BackupResponse newBackupResponse(Backup backup); + List listByOfferingId(Long backupOfferingId); + List listVmIdsWithBackupsInZone(Long zoneId); + public Long countBackupsForAccount(long accountId); + public Long calculateBackupStorageForAccount(long accountId); + void loadDetails(BackupVO backup); + void saveDetails(BackupVO backup); + List listBySchedule(Long backupScheduleId); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDaoImpl.java index fefbb68ae77c..fd29da72c718 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDaoImpl.java @@ -19,27 +19,31 @@ import java.util.ArrayList; import java.util.List; +import java.util.Map; import javax.annotation.PostConstruct; import javax.inject.Inject; -import org.apache.cloudstack.api.response.BackupResponse; +import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.storage.dao.VMTemplateDao; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericSearchBuilder; + import org.apache.cloudstack.backup.Backup; -import org.apache.cloudstack.backup.BackupOffering; +import org.apache.cloudstack.backup.BackupDetailVO; import org.apache.cloudstack.backup.BackupVO; +import org.apache.commons.collections.CollectionUtils; -import com.cloud.dc.DataCenterVO; import com.cloud.dc.dao.DataCenterDao; -import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; -import com.cloud.user.AccountVO; import com.cloud.user.dao.AccountDao; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; -import com.cloud.vm.VMInstanceVO; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallback; import com.cloud.vm.dao.VMInstanceDao; -import com.google.gson.Gson; +import com.cloud.network.dao.NetworkDao; public class BackupDaoImpl extends GenericDaoBase implements BackupDao { @@ -55,10 +59,26 @@ public class BackupDaoImpl extends GenericDaoBase implements Bac @Inject VMInstanceDao vmInstanceDao; + @Inject + private VMTemplateDao templateDao; + @Inject BackupOfferingDao backupOfferingDao; + @Inject + BackupDetailsDao backupDetailsDao; + + @Inject + ServiceOfferingDao serviceOfferingDao; + + @Inject + NetworkDao networkDao; + private SearchBuilder backupSearch; + private GenericSearchBuilder CountBackupsByAccount; + private GenericSearchBuilder CalculateBackupStorageByAccount; + private SearchBuilder listBackupsBySchedule; + private GenericSearchBuilder backupVmSearchInZone; public BackupDaoImpl() { } @@ -68,7 +88,34 @@ protected void init() { backupSearch = createSearchBuilder(); backupSearch.and("vm_id", backupSearch.entity().getVmId(), SearchCriteria.Op.EQ); 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.done(); + + backupVmSearchInZone = createSearchBuilder(Long.class); + backupVmSearchInZone.select(null, SearchCriteria.Func.DISTINCT, backupVmSearchInZone.entity().getVmId()); + backupVmSearchInZone.and("zone_id", backupVmSearchInZone.entity().getZoneId(), SearchCriteria.Op.EQ); + backupVmSearchInZone.done(); + + CountBackupsByAccount = createSearchBuilder(Long.class); + CountBackupsByAccount.select(null, SearchCriteria.Func.COUNT, null); + CountBackupsByAccount.and("account", CountBackupsByAccount.entity().getAccountId(), SearchCriteria.Op.EQ); + CountBackupsByAccount.and("status", CountBackupsByAccount.entity().getStatus(), SearchCriteria.Op.NIN); + CountBackupsByAccount.and("removed", CountBackupsByAccount.entity().getRemoved(), SearchCriteria.Op.NULL); + CountBackupsByAccount.done(); + + CalculateBackupStorageByAccount = createSearchBuilder(SumCount.class); + CalculateBackupStorageByAccount.select("sum", SearchCriteria.Func.SUM, CalculateBackupStorageByAccount.entity().getSize()); + CalculateBackupStorageByAccount.and("account", CalculateBackupStorageByAccount.entity().getAccountId(), SearchCriteria.Op.EQ); + CalculateBackupStorageByAccount.and("status", CalculateBackupStorageByAccount.entity().getStatus(), SearchCriteria.Op.NIN); + CalculateBackupStorageByAccount.and("removed", CalculateBackupStorageByAccount.entity().getRemoved(), SearchCriteria.Op.NULL); + CalculateBackupStorageByAccount.done(); + + listBackupsBySchedule = createSearchBuilder(); + listBackupsBySchedule.and("backup_schedule_id", listBackupsBySchedule.entity().getBackupScheduleId(), SearchCriteria.Op.EQ); + listBackupsBySchedule.and("status", listBackupsBySchedule.entity().getStatus(), SearchCriteria.Op.EQ); + listBackupsBySchedule.and("removed", listBackupsBySchedule.entity().getRemoved(), SearchCriteria.Op.NULL); + listBackupsBySchedule.done(); } @Override @@ -103,9 +150,13 @@ public List listByVmId(Long zoneId, Long vmId) { } @Override - public List listByOfferingId(Long offeringId) { + public List listByVmIdAndOffering(Long zoneId, Long vmId, Long offeringId) { SearchCriteria sc = backupSearch.create(); - sc.setParameters("offering_id", offeringId); + sc.setParameters("vm_id", vmId); + if (zoneId != null) { + sc.setParameters("zone_id", zoneId); + } + sc.setParameters("backup_offering_id", offeringId); return new ArrayList<>(listBy(sc)); } @@ -116,6 +167,18 @@ private Backup findByExternalId(Long zoneId, String externalId) { return findOneBy(sc); } + @Override + public List searchByVmIds(List vmIds) { + if (CollectionUtils.isEmpty(vmIds)) { + return new ArrayList<>(); + } + SearchBuilder sb = createSearchBuilder(); + sb.and("vmIds", sb.entity().getVmId(), SearchCriteria.Op.IN); + SearchCriteria sc = sb.create(); + sc.setParameters("vmIds", vmIds.toArray()); + return search(sc, null); + } + public BackupVO getBackupVO(Backup backup) { BackupVO backupVO = new BackupVO(); backupVO.setExternalId(backup.getExternalId()); @@ -123,6 +186,13 @@ public BackupVO getBackupVO(Backup backup) { return backupVO; } + @Override + public List listByOfferingId(Long backupOfferingId) { + SearchCriteria sc = backupSearch.create(); + sc.setParameters("backup_offering_id", backupOfferingId); + return new ArrayList<>(listBy(sc)); + } + public void removeExistingBackups(Long zoneId, Long vmId) { SearchCriteria sc = backupSearch.create(); sc.setParameters("vm_id", vmId); @@ -130,6 +200,27 @@ public void removeExistingBackups(Long zoneId, Long vmId) { expunge(sc); } + @Override + public BackupVO persist(BackupVO backup) { + return Transaction.execute((TransactionCallback) status -> { + BackupVO backupDb = super.persist(backup); + saveDetails(backup); + loadDetails(backupDb); + return backupDb; + }); + } + + @Override + public boolean update(Long id, BackupVO backup) { + return Transaction.execute((TransactionCallback) status -> { + boolean result = super.update(id, backup); + if (result) { + saveDetails(backup); + } + return result; + }); + } + @Override public List syncBackups(Long zoneId, Long vmId, List externalBackups) { for (Backup backup : externalBackups) { @@ -140,33 +231,53 @@ public List syncBackups(Long zoneId, Long vmId, List externalBac } @Override - public BackupResponse newBackupResponse(Backup backup) { - VMInstanceVO vm = vmInstanceDao.findByIdIncludingRemoved(backup.getVmId()); - AccountVO account = accountDao.findByIdIncludingRemoved(vm.getAccountId()); - DomainVO domain = domainDao.findByIdIncludingRemoved(vm.getDomainId()); - DataCenterVO zone = dataCenterDao.findByIdIncludingRemoved(vm.getDataCenterId()); - BackupOffering offering = backupOfferingDao.findByIdIncludingRemoved(vm.getBackupOfferingId()); - - BackupResponse response = new BackupResponse(); - response.setId(backup.getUuid()); - response.setVmId(vm.getUuid()); - response.setVmName(vm.getHostName()); - response.setExternalId(backup.getExternalId()); - response.setType(backup.getType()); - response.setDate(backup.getDate()); - response.setSize(backup.getSize()); - response.setProtectedSize(backup.getProtectedSize()); - response.setStatus(backup.getStatus()); - response.setVolumes(new Gson().toJson(vm.getBackupVolumeList().toArray(), Backup.VolumeInfo[].class)); - response.setBackupOfferingId(offering.getUuid()); - response.setBackupOffering(offering.getName()); - response.setAccountId(account.getUuid()); - response.setAccount(account.getAccountName()); - response.setDomainId(domain.getUuid()); - response.setDomain(domain.getName()); - response.setZoneId(zone.getUuid()); - response.setZone(zone.getName()); - response.setObjectName("backup"); - return response; + public Long countBackupsForAccount(long accountId) { + SearchCriteria sc = CountBackupsByAccount.create(); + sc.setParameters("account", accountId); + sc.setParameters("status", Backup.Status.Failed, Backup.Status.Removed, Backup.Status.Expunged); + return customSearch(sc, null).get(0); + } + + @Override + public Long calculateBackupStorageForAccount(long accountId) { + SearchCriteria sc = CalculateBackupStorageByAccount.create(); + sc.setParameters("account", accountId); + sc.setParameters("status", Backup.Status.Failed, Backup.Status.Removed, Backup.Status.Expunged); + return customSearch(sc, null).get(0).sum; + } + + @Override + public List listBySchedule(Long backupScheduleId) { + SearchCriteria sc = listBackupsBySchedule.create(); + sc.setParameters("backup_schedule_id", backupScheduleId); + sc.setParameters("status", Backup.Status.BackedUp); + return listBy(sc, new Filter(BackupVO.class, "date", true)); + } + + @Override + public void loadDetails(BackupVO backup) { + Map details = backupDetailsDao.listDetailsKeyPairs(backup.getId()); + backup.setDetails(details); + } + + @Override + public void saveDetails(BackupVO backup) { + Map detailsStr = backup.getDetails(); + if (detailsStr == null) { + return; + } + List details = new ArrayList(); + for (String key : detailsStr.keySet()) { + BackupDetailVO detail = new BackupDetailVO(backup.getId(), key, detailsStr.get(key), true); + details.add(detail); + } + backupDetailsDao.saveDetails(details); + } + + @Override + public List listVmIdsWithBackupsInZone(Long zoneId) { + SearchCriteria sc = backupVmSearchInZone.create(); + sc.setParameters("zone_id", zoneId); + return customSearchIncludingRemoved(sc, null); } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDetailsDao.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDetailsDao.java new file mode 100644 index 000000000000..664650074bce --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDetailsDao.java @@ -0,0 +1,26 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.backup.dao; + +import org.apache.cloudstack.backup.BackupDetailVO; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; + +import com.cloud.utils.db.GenericDao; + +public interface BackupDetailsDao extends GenericDao, ResourceDetailsDao { + +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDetailsDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDetailsDaoImpl.java new file mode 100644 index 000000000000..08c7192af909 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDetailsDaoImpl.java @@ -0,0 +1,31 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.backup.dao; + + +import org.apache.cloudstack.backup.BackupDetailVO; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; +import org.springframework.stereotype.Component; + +@Component +public class BackupDetailsDaoImpl extends ResourceDetailsDaoBase implements BackupDetailsDao { + + @Override + public void addDetail(long resourceId, String key, String value, boolean display) { + super.addDetail(new BackupDetailVO(resourceId, key, value, display)); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/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 0568a0185bba..708faeef4643 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,6 +20,8 @@ import javax.annotation.PostConstruct; import javax.inject.Inject; +import com.cloud.domain.DomainVO; +import com.cloud.domain.dao.DomainDao; import org.apache.cloudstack.api.response.BackupOfferingResponse; import org.apache.cloudstack.backup.BackupOffering; import org.apache.cloudstack.backup.BackupOfferingVO; @@ -30,10 +32,16 @@ import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; +import java.util.List; + public class BackupOfferingDaoImpl extends GenericDaoBase implements BackupOfferingDao { @Inject DataCenterDao dataCenterDao; + @Inject + BackupOfferingDetailsDao backupOfferingDetailsDao; + @Inject + DomainDao domainDao; private SearchBuilder backupPoliciesSearch; @@ -50,19 +58,36 @@ 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()); BackupOfferingResponse response = new BackupOfferingResponse(); response.setId(offering.getUuid()); response.setName(offering.getName()); response.setDescription(offering.getDescription()); response.setExternalId(offering.getExternalId()); + response.setProvider(offering.getProvider()); response.setUserDrivenBackups(offering.isUserDrivenBackupAllowed()); if (zone != null) { 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); + } 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/BackupRepositoryDao.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupRepositoryDao.java new file mode 100644 index 000000000000..6dec994be0c9 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupRepositoryDao.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.backup.dao; + +import java.util.List; + +import org.apache.cloudstack.backup.BackupRepository; +import org.apache.cloudstack.backup.BackupRepositoryVO; + +import com.cloud.utils.db.GenericDao; + +public interface BackupRepositoryDao extends GenericDao { + List listByZoneAndProvider(Long zoneId, String provider); + + BackupRepository findByBackupOfferingId(Long backupOfferingId); + + boolean updateCapacity(BackupRepository backupRepository, Long capacityBytes, Long usedBytes); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupRepositoryDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupRepositoryDaoImpl.java new file mode 100644 index 000000000000..ea969988e2bb --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupRepositoryDaoImpl.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.backup.dao; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; + +import org.apache.cloudstack.backup.BackupOfferingVO; +import org.apache.cloudstack.backup.BackupRepository; +import org.apache.cloudstack.backup.BackupRepositoryVO; + +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +public class BackupRepositoryDaoImpl extends GenericDaoBase implements BackupRepositoryDao { + @Inject + BackupOfferingDao backupOfferingDao; + + private SearchBuilder backupRepoSearch; + + public BackupRepositoryDaoImpl() { + } + + @PostConstruct + protected void init() { + backupRepoSearch = createSearchBuilder(); + backupRepoSearch.and("zone_id", backupRepoSearch.entity().getZoneId(), SearchCriteria.Op.EQ); + backupRepoSearch.and("provider", backupRepoSearch.entity().getProvider(), SearchCriteria.Op.EQ); + backupRepoSearch.done(); + } + + @Override + public List listByZoneAndProvider(Long zoneId, String provider) { + SearchCriteria sc = backupRepoSearch.create(); + sc.setParameters("zone_id", zoneId); + sc.setParameters("provider", provider); + return new ArrayList<>(listBy(sc)); + } + + @Override + public BackupRepository findByBackupOfferingId(Long backupOfferingId) { + BackupOfferingVO offering = backupOfferingDao.findByIdIncludingRemoved(backupOfferingId); + if (offering == null) { + return null; + } + return findByUuid(offering.getExternalId()); + } + + @Override + public boolean updateCapacity(BackupRepository backupRepository, Long capacityBytes, Long usedBytes) { + BackupRepositoryVO repository = findById(backupRepository.getId()); + repository.setCapacityBytes(capacityBytes); + repository.setUsedBytes(usedBytes); + return update(repository.getId(), repository); + } +} 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 516b0112c986..ee1783a9c896 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 @@ -20,6 +20,7 @@ import java.util.Date; 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; @@ -29,6 +30,10 @@ 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 7a58679e7e53..d9cf7b63680b 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 @@ -23,6 +23,7 @@ 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 org.apache.cloudstack.backup.BackupScheduleVO; @@ -49,6 +50,7 @@ protected void init() { backupScheduleSearch = createSearchBuilder(); backupScheduleSearch.and("vm_id", backupScheduleSearch.entity().getVmId(), SearchCriteria.Op.EQ); backupScheduleSearch.and("async_job_id", backupScheduleSearch.entity().getAsyncJobId(), SearchCriteria.Op.EQ); + backupScheduleSearch.and("interval_type", backupScheduleSearch.entity().getScheduleType(), SearchCriteria.Op.EQ); backupScheduleSearch.done(); executableSchedulesSearch = createSearchBuilder(); @@ -64,6 +66,21 @@ public BackupScheduleVO findByVM(Long vmId) { return findOneBy(sc); } + @Override + public List listByVM(Long vmId) { + SearchCriteria sc = backupScheduleSearch.create(); + sc.setParameters("vm_id", vmId); + return listBy(sc, null); + } + + @Override + public BackupScheduleVO findByVMAndIntervalType(Long vmId, DateUtil.IntervalType intervalType) { + SearchCriteria sc = backupScheduleSearch.create(); + sc.setParameters("vm_id", vmId); + sc.setParameters("interval_type", intervalType.ordinal()); + return findOneBy(sc); + } + @Override public List getSchedulesToExecute(Date currentTimestamp) { SearchCriteria sc = executableSchedulesSearch.create(); @@ -75,11 +92,16 @@ public List getSchedulesToExecute(Date currentTimestamp) { 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()); + } response.setObjectName("backupschedule"); return response; } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/cluster/ClusterDrsPlanMigrationVO.java b/engine/schema/src/main/java/org/apache/cloudstack/cluster/ClusterDrsPlanMigrationVO.java index eab2e555d693..6afc2e7707a1 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/cluster/ClusterDrsPlanMigrationVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/cluster/ClusterDrsPlanMigrationVO.java @@ -20,6 +20,7 @@ package org.apache.cloudstack.cluster; import org.apache.cloudstack.jobs.JobInfo; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import javax.persistence.Column; import javax.persistence.Entity; @@ -66,6 +67,13 @@ protected ClusterDrsPlanMigrationVO() { } + @Override + public String toString() { + return String.format("ClusterDrsPlanMigration %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "planId", "vmId", "jobId")); + } + public long getId() { return id; } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/cluster/ClusterDrsPlanVO.java b/engine/schema/src/main/java/org/apache/cloudstack/cluster/ClusterDrsPlanVO.java index 0ce25ae90fe3..68f7fe4b44e8 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/cluster/ClusterDrsPlanVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/cluster/ClusterDrsPlanVO.java @@ -20,6 +20,7 @@ package org.apache.cloudstack.cluster; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import javax.persistence.Column; import javax.persistence.Entity; @@ -68,6 +69,13 @@ protected ClusterDrsPlanVO() { uuid = UUID.randomUUID().toString(); } + @Override + public String toString() { + return String.format("ClusterDrsPlan %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "clusterId")); + } + public long getId() { return id; } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/command/ReconcileCommandVO.java b/engine/schema/src/main/java/org/apache/cloudstack/command/ReconcileCommandVO.java new file mode 100644 index 000000000000..150c9662ada1 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/command/ReconcileCommandVO.java @@ -0,0 +1,216 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.command; + +import 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 com.cloud.agent.api.Command; +import com.cloud.utils.db.GenericDao; + +import org.apache.cloudstack.acl.InfrastructureEntity; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.InternalIdentity; + +@Entity +@Table(name = "reconcile_commands") +public class ReconcileCommandVO implements InfrastructureEntity, InternalIdentity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "management_server_id") + private long managementServerId; + + @Column(name = "host_id") + private long hostId; + + @Column(name = "request_sequence") + private long requestSequence; + + @Column(name = "resource_id") + private long resourceId; + + @Column(name = "resource_type") + private ApiCommandResourceType resourceType; + + @Column(name = "state_by_management") + private Command.State stateByManagement; + + @Column(name = "state_by_agent") + private Command.State stateByAgent; + + @Column(name = "command_name") + private String commandName; + + @Column(name = "command_info", length = 65535) + private String commandInfo; + + @Column(name = "answer_name") + private String answerName; + + @Column(name = "answer_info", length = 65535) + private String answerInfo; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + @Column(name= GenericDao.REMOVED_COLUMN) + private Date removed; + + @Temporal(TemporalType.TIMESTAMP) + @Column(name= "updated") + private Date updated; + + @Column(name= "retry_count") + private Long retryCount = 0L; + + @Override + public long getId() { + return id; + } + + public long getManagementServerId() { + return managementServerId; + } + + public void setManagementServerId(long managementServerId) { + this.managementServerId = managementServerId; + } + + public long getHostId() { + return hostId; + } + + public void setHostId(long hostId) { + this.hostId = hostId; + } + + public long getRequestSequence() { + return requestSequence; + } + + public void setRequestSequence(long requestSequence) { + this.requestSequence = requestSequence; + } + + public long getResourceId() { + return resourceId; + } + + public void setResourceId(long resourceId) { + this.resourceId = resourceId; + } + + public ApiCommandResourceType getResourceType() { + return resourceType; + } + + public void setResourceType(ApiCommandResourceType resourceType) { + this.resourceType = resourceType; + } + + public Command.State getStateByManagement() { + return stateByManagement; + } + + public void setStateByManagement(Command.State stateByManagement) { + this.stateByManagement = stateByManagement; + } + + public Command.State getStateByAgent() { + return stateByAgent; + } + + public void setStateByAgent(Command.State stateByAgent) { + this.stateByAgent = stateByAgent; + } + + public String getCommandName() { + return commandName; + } + + public void setCommandName(String commandName) { + this.commandName = commandName; + } + + public String getCommandInfo() { + return commandInfo; + } + + public void setCommandInfo(String commandInfo) { + this.commandInfo = commandInfo; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public String getAnswerName() { + return answerName; + } + + public void setAnswerName(String answerName) { + this.answerName = answerName; + } + + public String getAnswerInfo() { + return answerInfo; + } + + public void setAnswerInfo(String answerInfo) { + this.answerInfo = answerInfo; + } + + public Date getRemoved() { + return removed; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } + + public Date getUpdated() { + return updated; + } + + public void setUpdated(Date updated) { + this.updated = updated; + } + + public Long getRetryCount() { + return retryCount; + } + + public void setRetryCount(Long retryCount) { + this.retryCount = retryCount; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/command/dao/ReconcileCommandDao.java b/engine/schema/src/main/java/org/apache/cloudstack/command/dao/ReconcileCommandDao.java new file mode 100644 index 000000000000..59c4605d5487 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/command/dao/ReconcileCommandDao.java @@ -0,0 +1,45 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.command.dao; + +import com.cloud.agent.api.Command; +import com.cloud.utils.db.GenericDao; + +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.command.ReconcileCommandVO; + +import java.util.List; + +public interface ReconcileCommandDao extends GenericDao { + + List listByManagementServerId(long managementServerId); + + List listByHostId(long hostId); + + List listByState(Command.State... states); + + void removeCommand(long commandId, String commandName, Command.State state); + + ReconcileCommandVO findCommand(long reqSequence, String commandName); + + void updateCommandsToInterruptedByManagementServerId(long managementServerId); + + void updateCommandsToInterruptedByHostId(long hostId); + + List listByResourceIdAndTypeAndStates(long resourceId, ApiCommandResourceType resourceType, Command.State... states); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/command/dao/ReconcileCommandDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/command/dao/ReconcileCommandDaoImpl.java new file mode 100644 index 000000000000..593f464f9ad4 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/command/dao/ReconcileCommandDaoImpl.java @@ -0,0 +1,134 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.command.dao; + +import java.util.Date; +import java.util.List; + +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.command.ReconcileCommandVO; +import org.springframework.stereotype.Component; + +import com.cloud.agent.api.Command; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.QueryBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@Component +@DB +public class ReconcileCommandDaoImpl extends GenericDaoBase implements ReconcileCommandDao { + + final SearchBuilder updateCommandSearch; + final SearchBuilder resourceSearch; + + public ReconcileCommandDaoImpl() { + + updateCommandSearch = createSearchBuilder(); + updateCommandSearch.and("managementServerId", updateCommandSearch.entity().getManagementServerId(), SearchCriteria.Op.EQ); + updateCommandSearch.and("stateByManagement", updateCommandSearch.entity().getStateByManagement(), SearchCriteria.Op.IN); + updateCommandSearch.and("hostId", updateCommandSearch.entity().getHostId(), SearchCriteria.Op.EQ); + updateCommandSearch.and("stateByAgent", updateCommandSearch.entity().getStateByAgent(), SearchCriteria.Op.IN); + updateCommandSearch.and("reqSequence", updateCommandSearch.entity().getRequestSequence(), SearchCriteria.Op.EQ); + updateCommandSearch.and("commandName", updateCommandSearch.entity().getCommandName(), SearchCriteria.Op.EQ); + updateCommandSearch.done(); + + resourceSearch = createSearchBuilder(); + resourceSearch.and("resourceId", resourceSearch.entity().getResourceId(), SearchCriteria.Op.EQ); + resourceSearch.and("resourceType", resourceSearch.entity().getResourceType(), SearchCriteria.Op.EQ); + resourceSearch.and("stateByManagement", resourceSearch.entity().getStateByManagement(), SearchCriteria.Op.IN); + resourceSearch.done(); + } + + @Override + public List listByManagementServerId(long managementServerId) { + QueryBuilder sc = QueryBuilder.create(ReconcileCommandVO.class); + sc.and(sc.entity().getManagementServerId(), SearchCriteria.Op.EQ, managementServerId); + return sc.list(); + } + + @Override + public List listByHostId(long hostId) { + QueryBuilder sc = QueryBuilder.create(ReconcileCommandVO.class); + sc.and(sc.entity().getHostId(), SearchCriteria.Op.EQ, hostId); + return sc.list(); + } + + @Override + public List listByState(Command.State... states) { + QueryBuilder sc = QueryBuilder.create(ReconcileCommandVO.class); + sc.and(sc.entity().getStateByManagement(), SearchCriteria.Op.IN, (Object[]) states); + return sc.list(); + } + + @Override + public void removeCommand(long reqSequence, String commandName, Command.State state) { + SearchCriteria sc = updateCommandSearch.create(); + sc.setParameters("reqSequence", reqSequence); + sc.setParameters("commandName", commandName); + + ReconcileCommandVO vo = createForUpdate(); + if (state != null) { + vo.setStateByManagement(state); + } + vo.setRemoved(new Date()); + update(vo, sc); + } + + @Override + public ReconcileCommandVO findCommand(long reqSequence, String commandName) { + SearchCriteria sc = updateCommandSearch.create(); + sc.setParameters("reqSequence", reqSequence); + sc.setParameters("commandName", commandName); + return findOneBy(sc); + } + + @Override + public void updateCommandsToInterruptedByManagementServerId(long managementServerId) { + SearchCriteria sc = updateCommandSearch.create(); + sc.setParameters("managementServerId", managementServerId); + sc.setParameters("stateByManagement", Command.State.CREATED, Command.State.RECONCILING); + + ReconcileCommandVO vo = createForUpdate(); + vo.setStateByManagement(Command.State.INTERRUPTED); + + update(vo, sc); + } + + @Override + public void updateCommandsToInterruptedByHostId(long hostId) { + SearchCriteria sc = updateCommandSearch.create(); + sc.setParameters("hostId", hostId); + sc.setParameters("stateByAgent", Command.State.STARTED, Command.State.PROCESSING, Command.State.PROCESSING_IN_BACKEND); + + ReconcileCommandVO vo = createForUpdate(); + vo.setStateByAgent(Command.State.INTERRUPTED); + + update(vo, sc); + } + + @Override + public List listByResourceIdAndTypeAndStates(long resourceId, ApiCommandResourceType resourceType, Command.State... states) { + QueryBuilder sc = QueryBuilder.create(ReconcileCommandVO.class); + sc.and(sc.entity().getResourceId(), SearchCriteria.Op.EQ, resourceId); + sc.and(sc.entity().getResourceType(), SearchCriteria.Op.EQ, resourceType); + sc.and(sc.entity().getStateByManagement(), SearchCriteria.Op.IN, (Object[]) states); + return sc.list(); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/datacenter/DataCenterIpv4GuestSubnetVO.java b/engine/schema/src/main/java/org/apache/cloudstack/datacenter/DataCenterIpv4GuestSubnetVO.java new file mode 100644 index 000000000000..828e7b39e9a4 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/datacenter/DataCenterIpv4GuestSubnetVO.java @@ -0,0 +1,123 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.datacenter; + +import java.util.Date; +import java.util.UUID; + +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 com.cloud.utils.db.GenericDao; + +@Entity +@Table(name = "dc_ip4_guest_subnets") +public class DataCenterIpv4GuestSubnetVO implements DataCenterIpv4GuestSubnet { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "uuid") + String uuid; + + @Column(name = "data_center_id") + private long dataCenterId; + + @Column(name = "subnet") + private String subnet; + + @Column(name = "domain_id") + Long domainId; + + @Column(name = "account_id") + Long accountId; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + @Column(name= GenericDao.REMOVED_COLUMN) + private Date removed; + + public DataCenterIpv4GuestSubnetVO(long dcId, String subnet) { + this(); + this.dataCenterId = dcId; + this.subnet = subnet; + this.created = new Date(); + } + + protected DataCenterIpv4GuestSubnetVO() { + this.uuid = UUID.randomUUID().toString(); + } + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public Long getDataCenterId() { + return dataCenterId; + } + + public void setDataCenterId(long dcId) { + this.dataCenterId = dcId; + } + + public String getSubnet() { + return subnet; + } + + public void setSubnet(String subnet) { + this.subnet = subnet; + } + + @Override + public Long getDomainId() { + return domainId; + } + + public void setDomainId(Long domainId) { + this.domainId = domainId; + } + + @Override + public Long getAccountId() { + return accountId; + } + + public void setAccountId(Long accountId) { + this.accountId = accountId; + } + + @Override + public Date getCreated() { + return created; + } + + +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/datacenter/dao/DataCenterIpv4GuestSubnetDao.java b/engine/schema/src/main/java/org/apache/cloudstack/datacenter/dao/DataCenterIpv4GuestSubnetDao.java new file mode 100644 index 000000000000..e231b267fda9 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/datacenter/dao/DataCenterIpv4GuestSubnetDao.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.datacenter.dao; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnetVO; + +import java.util.List; + +public interface DataCenterIpv4GuestSubnetDao extends GenericDao { + + List listByDataCenterId(long dcId); + List listByDataCenterIdAndAccountId(long dcId, long accountId); + List listByDataCenterIdAndDomainId(long dcId, long domainId); + List listNonDedicatedByDataCenterId(long dcId); + List listByAccountId(long accountId); + List listByDomainId(long domainId); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/datacenter/dao/DataCenterIpv4GuestSubnetDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/datacenter/dao/DataCenterIpv4GuestSubnetDaoImpl.java new file mode 100644 index 000000000000..49e8a6ef6626 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/datacenter/dao/DataCenterIpv4GuestSubnetDaoImpl.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.datacenter.dao; + +import java.util.List; + +import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnetVO; +import org.springframework.stereotype.Component; + +import com.cloud.utils.db.DB; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.QueryBuilder; +import com.cloud.utils.db.SearchCriteria; + +@Component +@DB +public class DataCenterIpv4GuestSubnetDaoImpl extends GenericDaoBase implements DataCenterIpv4GuestSubnetDao { + + public DataCenterIpv4GuestSubnetDaoImpl() { + } + + @Override + public List listByDataCenterId(long dcId) { + QueryBuilder sc = QueryBuilder.create(DataCenterIpv4GuestSubnetVO.class); + sc.and(sc.entity().getDataCenterId(), SearchCriteria.Op.EQ, dcId); + return sc.list(); + } + + @Override + public List listByDataCenterIdAndAccountId(long dcId, long accountId) { + QueryBuilder sc = QueryBuilder.create(DataCenterIpv4GuestSubnetVO.class); + sc.and(sc.entity().getDataCenterId(), SearchCriteria.Op.EQ, dcId); + sc.and(sc.entity().getAccountId(), SearchCriteria.Op.EQ, accountId); + return sc.list(); + } + + @Override + public List listByDataCenterIdAndDomainId(long dcId, long domainId) { + QueryBuilder sc = QueryBuilder.create(DataCenterIpv4GuestSubnetVO.class); + sc.and(sc.entity().getDataCenterId(), SearchCriteria.Op.EQ, dcId); + sc.and(sc.entity().getDomainId(), SearchCriteria.Op.EQ, domainId); + sc.and(sc.entity().getAccountId(), SearchCriteria.Op.NULL); + return sc.list(); + } + + @Override + public List listNonDedicatedByDataCenterId(long dcId) { + QueryBuilder sc = QueryBuilder.create(DataCenterIpv4GuestSubnetVO.class); + sc.and(sc.entity().getDataCenterId(), SearchCriteria.Op.EQ, dcId); + sc.and(sc.entity().getDomainId(), SearchCriteria.Op.NULL); + sc.and(sc.entity().getAccountId(), SearchCriteria.Op.NULL); + return sc.list(); + } + + @Override + public List listByAccountId(long accountId) { + QueryBuilder sc = QueryBuilder.create(DataCenterIpv4GuestSubnetVO.class); + sc.and(sc.entity().getAccountId(), SearchCriteria.Op.EQ, accountId); + return sc.list(); + } + + @Override + public List listByDomainId(long domainId) { + QueryBuilder sc = QueryBuilder.create(DataCenterIpv4GuestSubnetVO.class); + sc.and(sc.entity().getDomainId(), SearchCriteria.Op.EQ, domainId); + return sc.list(); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateVO.java b/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateVO.java index 1e43e164ba40..3c35f59659f6 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/direct/download/DirectDownloadCertificateVO.java @@ -17,8 +17,11 @@ package org.apache.cloudstack.direct.download; import com.cloud.hypervisor.Hypervisor; +import org.apache.cloudstack.util.HypervisorTypeConverter; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import javax.persistence.Column; +import javax.persistence.Convert; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; @@ -45,6 +48,7 @@ public class DirectDownloadCertificateVO implements DirectDownloadCertificate { private String certificate; @Column(name = "hypervisor_type") + @Convert(converter = HypervisorTypeConverter.class) private Hypervisor.HypervisorType hypervisorType; @Column(name = "zone_id") @@ -54,6 +58,13 @@ public DirectDownloadCertificateVO() { this.uuid = UUID.randomUUID().toString(); } + @Override + public String toString() { + return String.format("DirectDownloadCertificate %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "alias")); + } + public void setId(Long id) { this.id = id; } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/engine/cloud/entity/api/db/VMEntityVO.java b/engine/schema/src/main/java/org/apache/cloudstack/engine/cloud/entity/api/db/VMEntityVO.java index 4b96dd1641a8..917f8bb800a2 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/engine/cloud/entity/api/db/VMEntityVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/engine/cloud/entity/api/db/VMEntityVO.java @@ -24,6 +24,7 @@ import java.util.UUID; import javax.persistence.Column; +import javax.persistence.Convert; import javax.persistence.DiscriminatorColumn; import javax.persistence.DiscriminatorType; import javax.persistence.Entity; @@ -48,6 +49,8 @@ import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.State; import com.google.gson.Gson; +import org.apache.cloudstack.util.HypervisorTypeConverter; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "vm_instance") @@ -146,7 +149,7 @@ public class VMEntityVO implements VirtualMachine, FiniteStateObject details) { @Override public String toString() { if (toString == null) { - toString = new StringBuilder("VM[").append(type.toString()).append("|").append(hostName).append("]").toString(); + toString = String.format("VM %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "uuid", "instanceName", "type")); } return toString; } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeDetailsVO.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeDetailsVO.java new file mode 100644 index 000000000000..046a19c59fa0 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeDetailsVO.java @@ -0,0 +1,91 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gui.theme; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "gui_themes_details") +public class GuiThemeDetailsVO implements GuiThemeDetails { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "gui_theme_id", nullable = false) + private Long guiThemeId; + + @Column(name = "type", nullable = false, length = 100) + private String type; + + @Column(name = "value", nullable = false, length = 65535) + private String value; + + public GuiThemeDetailsVO() { + } + + public GuiThemeDetailsVO(Long guiThemeId, String type, String value) { + this.guiThemeId = guiThemeId; + this.type = type; + this.value = value; + } + + @Override + public long getId() { + return id; + } + + @Override + public void setId(Long id) { + this.id = id; + } + + @Override + public Long getGuiThemeId() { + return guiThemeId; + } + + @Override + public void setGuiThemeId(Long guiThemeId) { + this.guiThemeId = guiThemeId; + } + + @Override + public String getType() { + return type; + } + + @Override + public void setType(String type) { + this.type = type; + } + + @Override + public String getValue() { + return value; + } + + @Override + public void setValue(String value) { + this.value = value; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoinVO.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoinVO.java new file mode 100644 index 000000000000..2df23b3d1064 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoinVO.java @@ -0,0 +1,141 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gui.theme; + +import com.cloud.utils.db.GenericDao; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import java.util.Date; + +@Entity +@Table(name = "gui_themes_view") +public class GuiThemeJoinVO implements GuiThemeJoin { + @Id + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "uuid", nullable = false) + private String uuid; + + @Column(name = "name", nullable = false, length = 2048) + private String name; + + @Column(name = "description", length = 4096) + private String description; + + @Column(name = "css", nullable = false, length = 65535) + private String css; + + @Column(name = "json_configuration", nullable = false, length = 65535) + private String jsonConfiguration; + + @Column(name = "common_names", length = 65535) + private String commonNames; + + @Column(name = "domains", length = 65535) + private String domains; + + @Column(name = "accounts", length = 65535) + private String accounts; + + @Column(name = "recursive_domains") + private boolean recursiveDomains; + + @Column(name = "is_public") + private boolean isPublic; + + @Column(name = GenericDao.CREATED_COLUMN, nullable = false) + @Temporal(value = TemporalType.TIMESTAMP) + private Date created; + + @Column(name = GenericDao.REMOVED_COLUMN) + @Temporal(value = TemporalType.TIMESTAMP) + private Date removed = null; + + public GuiThemeJoinVO() { + } + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public String getCss() { + return css; + } + + @Override + public String getJsonConfiguration() { + return jsonConfiguration; + } + + @Override + public String getCommonNames() { + return commonNames; + } + + @Override + public String getDomains() { + return domains; + } + + @Override + public String getAccounts() { + return accounts; + } + + @Override + public boolean isRecursiveDomains() { + return recursiveDomains; + } + + @Override + public boolean getIsPublic() { + return isPublic; + } + + @Override + public Date getCreated() { + return created; + } + + @Override + public Date getRemoved() { + return removed; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeVO.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeVO.java new file mode 100644 index 000000000000..887e3886f6c6 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeVO.java @@ -0,0 +1,189 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gui.theme; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import java.util.Date; +import java.util.UUID; + +@Entity +@Table(name = "gui_themes") +public class GuiThemeVO implements GuiTheme { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "uuid", nullable = false) + private String uuid = UUID.randomUUID().toString(); + + @Column(name = "name", nullable = false, length = 2048) + private String name; + + @Column(name = "description", length = 4096) + private String description; + + @Column(name = "css", length = 65535) + private String css; + + @Column(name = "json_configuration", length = 65535) + private String jsonConfiguration; + + @Column(name = "is_public") + private boolean isPublic; + + @Column(name = "recursive_domains") + private boolean recursiveDomains = false; + + @Column(name = GenericDao.CREATED_COLUMN, nullable = false) + @Temporal(value = TemporalType.TIMESTAMP) + private Date created; + + @Column(name = GenericDao.REMOVED_COLUMN) + @Temporal(value = TemporalType.TIMESTAMP) + private Date removed = null; + + public GuiThemeVO() { + + } + + public GuiThemeVO(String name, String description, String css, String jsonConfiguration, boolean recursiveDomains, boolean isPublic, Date created, Date removed) { + this.name = name; + this.description = description; + this.css = css; + this.jsonConfiguration = jsonConfiguration; + this.recursiveDomains = recursiveDomains; + this.isPublic = isPublic; + this.created = created; + this.removed = removed; + } + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public String getCss() { + return css; + } + + @Override + public String getJsonConfiguration() { + return jsonConfiguration; + } + + @Override + public Date getCreated() { + return created; + } + + @Override + public Date getRemoved() { + return removed; + } + + @Override + public boolean getIsPublic() { + return isPublic; + } + + @Override + public void setId(Long id) { + this.id = id; + } + + @Override + public void setUuid(String uuid) { + this.uuid = uuid; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public void setDescription(String description) { + this.description = description; + } + + @Override + public void setCss(String css) { + this.css = css; + } + + @Override + public void setJsonConfiguration(String jsonConfiguration) { + this.jsonConfiguration = jsonConfiguration; + } + + @Override + public void setCreated(Date created) { + this.created = created; + } + + @Override + public void setRemoved(Date removed) { + this.removed = removed; + } + + @Override + public void setIsPublic(boolean isPublic) { + this.isPublic = isPublic; + } + + @Override + public boolean isRecursiveDomains() { + return recursiveDomains; + } + + @Override + public void setRecursiveDomains(boolean recursiveDomains) { + this.recursiveDomains = recursiveDomains; + } + + @Override + public String toString() { + return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "uuid", "name", "description", "isPublic", "recursiveDomains"); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDao.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDao.java new file mode 100644 index 000000000000..c00aedcc7866 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDao.java @@ -0,0 +1,24 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gui.theme.dao; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.gui.theme.GuiThemeVO; + +public interface GuiThemeDao extends GenericDao { + +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java new file mode 100644 index 000000000000..bc58c5f80f30 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java @@ -0,0 +1,25 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gui.theme.dao; + +import com.cloud.utils.db.GenericDaoBase; +import org.apache.cloudstack.gui.theme.GuiThemeVO; +import org.springframework.stereotype.Component; + +@Component +public class GuiThemeDaoImpl extends GenericDaoBase implements GuiThemeDao { +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDao.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDao.java new file mode 100644 index 000000000000..af243b1ffa46 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDao.java @@ -0,0 +1,30 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gui.theme.dao; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.gui.theme.GuiThemeDetailsVO; + +import java.util.List; + +public interface GuiThemeDetailsDao extends GenericDao { + List listGuiThemeIdsByCommonName(String commonName); + + List listGuiThemeIdsByDomainUuids(String domainUuid); + + void expungeByGuiThemeId(long guiThemeId); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDaoImpl.java new file mode 100644 index 000000000000..b0969833eb01 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDaoImpl.java @@ -0,0 +1,126 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gui.theme.dao; + +import com.cloud.domain.DomainVO; +import com.cloud.domain.dao.DomainDao; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.JoinBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.apache.cloudstack.gui.theme.GuiThemeDetailsVO; +import org.apache.cloudstack.gui.theme.GuiThemeVO; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +@Component +public class GuiThemeDetailsDaoImpl extends GenericDaoBase implements GuiThemeDetailsDao { + + @Inject + DomainDao domainDao; + + @Inject + GuiThemeDao guiThemeDao; + + public List listGuiThemeIdsByCommonName(String commonName) { + GenericSearchBuilder detailsDaoSearchBuilder = createSearchBuilder(Long.class); + detailsDaoSearchBuilder.selectFields(detailsDaoSearchBuilder.entity().getGuiThemeId()); + detailsDaoSearchBuilder.and("commonNameType", detailsDaoSearchBuilder.entity().getType(), SearchCriteria.Op.EQ); + detailsDaoSearchBuilder.and().op("firstReplace", detailsDaoSearchBuilder.entity().getValue(), SearchCriteria.Op.LIKE_REPLACE); + detailsDaoSearchBuilder.or("secondReplace", detailsDaoSearchBuilder.entity().getValue(), SearchCriteria.Op.LIKE_REPLACE).cp(); + detailsDaoSearchBuilder.done(); + + SearchCriteria searchCriteria = detailsDaoSearchBuilder.create(); + searchCriteria.setParameters("commonNameType", "commonName"); + searchCriteria.setParameters("firstReplace", commonName, "*", "%"); + searchCriteria.setParameters("secondReplace", commonName, "*.", "%"); + + return customSearch(searchCriteria, null); + } + + public List listGuiThemeIdsByDomainUuids(String domainUuid) { + List guiThemeIds = new ArrayList<>(); + String requestedDomainPath = domainDao.findByUuid(domainUuid).getPath(); + + SearchBuilder domainSearchBuilderPathLike = domainDao.createSearchBuilder(); + domainSearchBuilderPathLike.and("pathLike", domainSearchBuilderPathLike.entity().getPath(), SearchCriteria.Op.LIKE_CONCAT); + + SearchBuilder domainSearchBuilderPathEq = domainDao.createSearchBuilder(); + domainSearchBuilderPathEq.and("pathEq", domainSearchBuilderPathEq.entity().getPath(), SearchCriteria.Op.EQ); + + GenericSearchBuilder detailsSearchBuilderPathLike = createDetailsSearchBuilder(domainSearchBuilderPathLike); + SearchCriteria searchCriteriaDomainPathLike = setParametersDomainPathLike(detailsSearchBuilderPathLike, requestedDomainPath); + + GenericSearchBuilder detailsSearchBuilderPathEq = createDetailsSearchBuilder(domainSearchBuilderPathEq); + SearchCriteria searchCriteriaDomainPathEq = setParametersDomainPathEq(detailsSearchBuilderPathEq, requestedDomainPath); + + guiThemeIds.addAll(customSearch(searchCriteriaDomainPathLike, null)); + guiThemeIds.addAll(customSearch(searchCriteriaDomainPathEq, null)); + return guiThemeIds; + } + + private SearchCriteria setParametersDomainPathLike(GenericSearchBuilder detailsSearchBuilderPathLike, String requestedDomainPath) { + SearchCriteria searchCriteria = detailsSearchBuilderPathLike.create(); + searchCriteria.setParameters("domainUuidType", "domain"); + searchCriteria.setJoinParameters("domainJoin", "pathLike", requestedDomainPath, "%"); + searchCriteria.setJoinParameters("guiThemesJoin", "recursiveDomains", true); + + return searchCriteria; + } + + private SearchCriteria setParametersDomainPathEq(GenericSearchBuilder detailsSearchBuilderPathEq, String requestedDomainPath) { + SearchCriteria searchCriteria = detailsSearchBuilderPathEq.create(); + searchCriteria.setParameters("domainUuidType", "domain"); + searchCriteria.setJoinParameters("domainJoin", "pathEq", requestedDomainPath); + searchCriteria.setJoinParameters("guiThemesJoin", "recursiveDomains", false); + + return searchCriteria; + } + + private GenericSearchBuilder createDetailsSearchBuilder(SearchBuilder domainSearchBuilder) { + SearchBuilder guiThemeDaoSearchBuilder = guiThemeDao.createSearchBuilder(); + guiThemeDaoSearchBuilder.and("recursiveDomains", guiThemeDaoSearchBuilder.entity().isRecursiveDomains(), SearchCriteria.Op.EQ); + + GenericSearchBuilder guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder = createSearchBuilder(Long.class); + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.selectFields(guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.entity().getGuiThemeId()); + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.and("domainUuidType", guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.entity().getType(), SearchCriteria.Op.EQ); + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.join("domainJoin", domainSearchBuilder, domainSearchBuilder.entity().getUuid(), + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.entity().getValue(), JoinBuilder.JoinType.INNER); + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.join("guiThemesJoin", guiThemeDaoSearchBuilder, guiThemeDaoSearchBuilder.entity().getId(), + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.entity().getGuiThemeId(), JoinBuilder.JoinType.INNER); + + domainSearchBuilder.done(); + guiThemeDaoSearchBuilder.done(); + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.done(); + + return guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder; + } + + public void expungeByGuiThemeId(long guiThemeId) { + SearchBuilder searchBuilder = createSearchBuilder(); + searchBuilder.and("guiThemeId", searchBuilder.entity().getGuiThemeId(), SearchCriteria.Op.EQ); + searchBuilder.done(); + + SearchCriteria searchCriteria = searchBuilder.create(); + searchCriteria.setParameters("guiThemeId", guiThemeId); + expunge(searchCriteria); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDao.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDao.java new file mode 100644 index 000000000000..740199cfca73 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDao.java @@ -0,0 +1,31 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gui.theme.dao; + +import com.cloud.utils.Pair; +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.gui.theme.GuiThemeJoinVO; + +import java.util.List; + +public interface GuiThemeJoinDao extends GenericDao { + GuiThemeJoinVO findDefaultTheme(); + + Pair, Integer> listGuiThemesWithNoAuthentication(String commonName); + + Pair, Integer> listGuiThemes(Long id, String name, String commonName, String domainUuid, String accountUuid, boolean listAll, boolean showRemoved, Boolean showPublic); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDaoImpl.java new file mode 100644 index 000000000000..ce6f70558128 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDaoImpl.java @@ -0,0 +1,139 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.gui.theme.dao; + +import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.gui.theme.GuiThemeJoinVO; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +@Component +public class GuiThemeJoinDaoImpl extends GenericDaoBase implements GuiThemeJoinDao { + @Inject + GuiThemeDetailsDao guiThemeDetailsDao; + + public static final Long INVALID_ID = -1L; + + public GuiThemeJoinVO findDefaultTheme() { + SearchBuilder searchBuilder = createSearchBuilder(); + searchBuilder.and("commonNames", searchBuilder.entity().getCommonNames(), SearchCriteria.Op.NULL); + searchBuilder.and("domainUuids", searchBuilder.entity().getDomains(), SearchCriteria.Op.NULL); + searchBuilder.and("accountUuids", searchBuilder.entity().getAccounts(), SearchCriteria.Op.NULL); + searchBuilder.done(); + + SearchCriteria searchCriteria = searchBuilder.create(); + + return findOneBy(searchCriteria); + } + + public Pair, Integer> listGuiThemesWithNoAuthentication(String commonName) { + SearchCriteria searchCriteria = createGuiThemeSearchCriteria(null, null, commonName, null, null, null, false); + return searchOrderByCreatedDate(searchCriteria, false); + } + + public Pair, Integer> listGuiThemes(Long id, String name, String commonName, String domainUuid, String accountUuid, boolean listAll, + boolean showRemoved, Boolean showPublic) { + SearchCriteria searchCriteria = createGuiThemeSearchCriteria(id, name, commonName, domainUuid, accountUuid, showPublic, listAll); + + if (listAll) { + showRemoved = false; + } + + return searchOrderByCreatedDate(searchCriteria, showRemoved); + } + + private Pair, Integer> searchOrderByCreatedDate(SearchCriteria searchCriteria, boolean showRemoved) { + Filter filter = new Filter(GuiThemeJoinVO.class, "created", false); + return searchAndCount(searchCriteria, filter, showRemoved); + } + + private SearchCriteria createGuiThemeSearchCriteria(Long id, String name, String commonName, String domainUuid, String accountUuid, Boolean showPublic, boolean listAll) { + SearchCriteria searchCriteria = createGuiThemeJoinSearchBuilder(listAll, showPublic).create(); + List idList = new ArrayList<>(); + + if (id != null) { + idList.add(id); + } + + searchCriteria.setParametersIfNotNull("name", name); + searchCriteria.setParametersIfNotNull("isPublic", showPublic); + + if (StringUtils.isNotBlank(accountUuid)) { + searchCriteria.setParameters("accountUuid", "%" + accountUuid + "%"); + } + + if (StringUtils.isNotBlank(commonName)) { + setGuiThemeIdsFilteredByType(idList, ApiConstants.COMMON_NAME, commonName); + } + + if (StringUtils.isNotBlank(domainUuid)) { + setGuiThemeIdsFilteredByType(idList, ApiConstants.DOMAIN, domainUuid); + } + + searchCriteria.setParametersIfNotNull("idIn", idList.toArray()); + + return searchCriteria; + } + + /** + * Sets the `id IN ( )` clause of the query. If the informed value of common name or domain ID does not retrieve any GUI theme ID; then, an invalid ID (-1) is passed to the + * list, as not a single entity has this ID. This is necessary as to set the parameter even if it did not find any GUI theme ID; otherwise, the query would not filter the + * common name or domain ID passed. + */ + public void setGuiThemeIdsFilteredByType(List idList, String type, String value) { + List guiThemeIdsFilteredByType = new ArrayList<>(); + + switch (type) { + case ApiConstants.COMMON_NAME: + guiThemeIdsFilteredByType = guiThemeDetailsDao.listGuiThemeIdsByCommonName(value); + break; + case ApiConstants.DOMAIN: + guiThemeIdsFilteredByType = guiThemeDetailsDao.listGuiThemeIdsByDomainUuids(value); + break; + } + + if (CollectionUtils.isNotEmpty(guiThemeIdsFilteredByType)) { + idList.addAll(guiThemeIdsFilteredByType); + return; + } + logger.trace(String.format("No GUI theme with the specified [%s] with UUID [%s] was found, adding an invalid ID for filtering.", type, value)); + idList.add(INVALID_ID); + } + + private SearchBuilder createGuiThemeJoinSearchBuilder(boolean listAll, Boolean showPublic) { + SearchBuilder guiThemeJoinSearchBuilder = createSearchBuilder(); + guiThemeJoinSearchBuilder.and("idIn", guiThemeJoinSearchBuilder.entity().getId(), SearchCriteria.Op.IN); + guiThemeJoinSearchBuilder.and("name", guiThemeJoinSearchBuilder.entity().getName(), SearchCriteria.Op.EQ); + guiThemeJoinSearchBuilder.and("accountUuid", guiThemeJoinSearchBuilder.entity().getAccounts(), SearchCriteria.Op.LIKE); + + if (!listAll && showPublic != null) { + guiThemeJoinSearchBuilder.and("isPublic", guiThemeJoinSearchBuilder.entity().getIsPublic(), SearchCriteria.Op.EQ); + } + + return guiThemeJoinSearchBuilder; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/lb/ApplicationLoadBalancerRuleVO.java b/engine/schema/src/main/java/org/apache/cloudstack/lb/ApplicationLoadBalancerRuleVO.java index d8ee8631b0b9..4fec96067a36 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/lb/ApplicationLoadBalancerRuleVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/lb/ApplicationLoadBalancerRuleVO.java @@ -30,6 +30,7 @@ import com.cloud.network.rules.FirewallRuleVO; import com.cloud.utils.net.Ip; import com.cloud.utils.net.NetUtils; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; /** * This VO represent Internal Load Balancer rule. @@ -90,6 +91,13 @@ public ApplicationLoadBalancerRuleVO(String name, String description, int srcPor this.scheme = scheme; } + @Override + public String toString() { + return String.format("ApplicationLoadBalancerRule %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "purpose", "state")); + } + @Override public Long getSourceIpNetworkId() { return sourceIpNetworkId; diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/BgpPeerDetailsVO.java b/engine/schema/src/main/java/org/apache/cloudstack/network/BgpPeerDetailsVO.java new file mode 100644 index 000000000000..9e3378870113 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/BgpPeerDetailsVO.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 +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.network; + +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 org.apache.cloudstack.api.ResourceDetail; + +@Entity +@Table(name = "bgp_peer_details") +public class BgpPeerDetailsVO implements ResourceDetail { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "bgp_peer_id") + private long resourceId; + + @Enumerated(value = EnumType.STRING) + @Column(name = "name") + private BgpPeer.Detail name; + + @Column(name = "value", length = 1024) + private String value; + + @Column(name = "display") + private boolean display; + + public BgpPeerDetailsVO() { + } + + public BgpPeerDetailsVO(long resourceId, BgpPeer.Detail detailName, String value, boolean display) { + this.resourceId = resourceId; + this.name = detailName; + this.value = value; + this.display = display; + } + + @Override + public long getId() { + return id; + } + + @Override + public long getResourceId() { + return resourceId; + } + + public void setResourceId(long resourceId) { + this.resourceId = resourceId; + } + + public String getName() { + return name.name(); + } + + public BgpPeer.Detail getDetailName() { + return name; + } + + public String getValue() { + return value; + } + + @Override + public boolean isDisplay() { + return display; + } + + public void setId(long id) { + this.id = id; + } + + public void setName(BgpPeer.Detail name) { + this.name = name; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/BgpPeerNetworkMapVO.java b/engine/schema/src/main/java/org/apache/cloudstack/network/BgpPeerNetworkMapVO.java new file mode 100644 index 000000000000..b520ecd5cd1a --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/BgpPeerNetworkMapVO.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.network; + +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 com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +@Entity +@Table(name = "bgp_peer_network_map") +public class BgpPeerNetworkMapVO implements InternalIdentity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "bgp_peer_id") + private long bgpPeerId; + + @Column(name = "network_id") + private Long networkId; + + @Column(name = "vpc_id") + private Long vpcId; + + @Column(name = "state") + private BgpPeer.State state; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + @Column(name= GenericDao.REMOVED_COLUMN) + private Date removed; + + /** + * There should never be a public constructor for this class. Since it's + * only here to define the table for the DAO class. + */ + protected BgpPeerNetworkMapVO() { + } + + public BgpPeerNetworkMapVO(long bgpPeerId, Long networkId, Long vpcId, BgpPeer.State state) { + this.bgpPeerId = bgpPeerId; + this.networkId = networkId; + this.vpcId = vpcId; + this.state = state; + } + + @Override + public long getId() { + return id; + } + + public long getBgpPeerId() { + return bgpPeerId; + } + + public Long getNetworkId() { + return networkId; + } + + public Long getVpcId() { + return vpcId; + } + + public BgpPeer.State getState() { + return state; + } + + public void setState(BgpPeer.State state) { + this.state = state; + } + + public Date getCreated() { + return created; + } + + public Date getRemoved() { + return removed; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/BgpPeerVO.java b/engine/schema/src/main/java/org/apache/cloudstack/network/BgpPeerVO.java new file mode 100644 index 000000000000..c60a3ec38683 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/BgpPeerVO.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 org.apache.cloudstack.network; + +import java.util.Date; +import java.util.UUID; + +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 com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; + +@Entity +@Table(name = "bgp_peers") +public class BgpPeerVO implements BgpPeer { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "data_center_id") + private long dataCenterId; + + @Column(name = "ip4_address") + private String ip4Address; + + @Column(name = "ip6_address") + private String ip6Address; + + @Column(name = "as_number") + private Long asNumber; + + @Column(name = "password") + private String password; + + @Column(name = "domain_id") + Long domainId; + + @Column(name = "account_id") + Long accountId; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + @Column(name= GenericDao.REMOVED_COLUMN) + private Date removed; + + protected BgpPeerVO() { + uuid = UUID.randomUUID().toString(); + } + + public BgpPeerVO(long dcId, String ip4Address, String ip6Address, Long asNumber, String password) { + this(); + this.dataCenterId = dcId; + this.ip4Address = ip4Address; + this.ip6Address = ip6Address; + this.asNumber = asNumber; + this.password = password; + } + + @Override + public String toString() { + return String.format("BgpPeer %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "asNumber", "ip4Address", "ip6Address")); + } + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public long getDataCenterId() { + return dataCenterId; + } + + public void setDataCenterId(long dataCenterId) { + this.dataCenterId = dataCenterId; + } + + @Override + public String getIp4Address() { + return ip4Address; + } + + public void setIp4Address(String ip4Address) { + this.ip4Address = ip4Address; + } + + @Override + public String getIp6Address() { + return ip6Address; + } + + public void setIp6Address(String ip6Address) { + this.ip6Address = ip6Address; + } + + @Override + public Long getAsNumber() { + return asNumber; + } + + public void setAsNumber(Long asNumber) { + this.asNumber = asNumber; + } + + @Override + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @Override + public Long getDomainId() { + return domainId; + } + + public void setDomainId(Long domainId) { + this.domainId = domainId; + } + + @Override + public Long getAccountId() { + return accountId; + } + + public void setAccountId(Long accountId) { + this.accountId = accountId; + } + + @Override + public Date getCreated() { + return created; + } + + public Date getRemoved() { + return removed; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/Ipv4GuestSubnetNetworkMapVO.java b/engine/schema/src/main/java/org/apache/cloudstack/network/Ipv4GuestSubnetNetworkMapVO.java new file mode 100644 index 000000000000..cc726ba3d357 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/Ipv4GuestSubnetNetworkMapVO.java @@ -0,0 +1,143 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.network; + +import java.util.Date; +import java.util.UUID; + +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 com.cloud.utils.db.GenericDao; + +@Entity +@Table(name = "ip4_guest_subnet_network_map") +public class Ipv4GuestSubnetNetworkMapVO implements Ipv4GuestSubnetNetworkMap { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "parent_id") + private Long parentId; + + @Column(name = "subnet") + private String subnet; + + @Column(name = "vpc_id") + private Long vpcId; + + @Column(name = "network_id") + private Long networkId; + + @Column(name = "state") + private State state; + + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "allocated") + Date allocated; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + @Column(name= GenericDao.REMOVED_COLUMN) + private Date removed; + + protected Ipv4GuestSubnetNetworkMapVO() { + uuid = UUID.randomUUID().toString(); + } + + protected Ipv4GuestSubnetNetworkMapVO(Long parentId, String subnet, Long networkId, Ipv4GuestSubnetNetworkMap.State state) { + this.parentId = parentId; + this.subnet = subnet; + this.networkId = networkId; + this.state = state; + uuid = UUID.randomUUID().toString(); + } + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public Long getParentId() { + return parentId; + } + + @Override + public String getSubnet() { + return subnet; + } + + @Override + public Long getVpcId() { + return vpcId; + } + + public void setVpcId(Long vpcId) { + this.vpcId = vpcId; + } + + @Override + public Long getNetworkId() { + return networkId; + } + + public void setNetworkId(Long networkId) { + this.networkId = networkId; + } + + @Override + public State getState() { + return state; + } + + public void setState(Ipv4GuestSubnetNetworkMap.State state) { + this.state = state; + } + + public void setAllocated(Date allocated) { + this.allocated = allocated; + } + + @Override + public Date getAllocated() { + return allocated; + } + + @Override + public Date getCreated() { + return created; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerDao.java b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerDao.java new file mode 100644 index 000000000000..8ca4c2d86da7 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerDao.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.network.dao; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.network.BgpPeer; +import org.apache.cloudstack.network.BgpPeerVO; + +import java.util.List; +import java.util.Map; + +public interface BgpPeerDao extends GenericDao { + List listNonRevokeByNetworkId(long networkId); + + List listNonRevokeByVpcId(long vpcId); + + BgpPeerVO findByZoneAndAsNumberAndAddress(long zoneId, Long asNumber, String ip4Address, String ip6Address); + + BgpPeerVO persist(BgpPeerVO bgpPeerVO, Map details); + + List listAvailableBgpPeerIdsForAccount(long zoneId, long domainId, long accountId, boolean useSystemBgpPeers); + + int removeByAccountId(long accountId); + int removeByDomainId(long domainId); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerDaoImpl.java new file mode 100644 index 000000000000..0f95f7c3cd58 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerDaoImpl.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.network.dao; + +import com.cloud.utils.db.DB; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.JoinBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +import com.cloud.utils.db.TransactionLegacy; +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.cloudstack.network.BgpPeer; +import org.apache.cloudstack.network.BgpPeerDetailsVO; +import org.apache.cloudstack.network.BgpPeerNetworkMapVO; +import org.apache.cloudstack.network.BgpPeerVO; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Component +@DB +public class BgpPeerDaoImpl extends GenericDaoBase implements BgpPeerDao { + protected SearchBuilder NetworkIdSearch; + protected SearchBuilder VpcIdSearch; + protected SearchBuilder AllFieldsSearch; + + private static final String LIST_ALL_BGP_PEERS_IDS_FOR_ACCOUNT = "SELECT id FROM `cloud`.`bgp_peers` WHERE removed IS NULL AND data_center_id = ? " + + "AND ((domain_id IS NULL AND account_id IS NULL) " + + "OR (domain_id = ? AND account_id IS NULL) " + + "OR (domain_id = ? AND account_id = ?))"; + + private static final String LIST_DEDICATED_BGP_PEERS_IDS_FOR_ACCOUNT = "SELECT id FROM `cloud`.`bgp_peers` WHERE removed IS NULL AND data_center_id = ? " + + "AND ((domain_id = ? AND account_id IS NULL) " + + "OR (domain_id = ? AND account_id = ?))"; + + @Inject + BgpPeerNetworkMapDao bgpPeerNetworkMapDao; + @Inject + BgpPeerDetailsDao bgpPeerDetailsDao; + + @PostConstruct + public void init() { + final SearchBuilder networkSearchBuilder = bgpPeerNetworkMapDao.createSearchBuilder(); + networkSearchBuilder.and("networkId", networkSearchBuilder.entity().getNetworkId(), SearchCriteria.Op.EQ); + networkSearchBuilder.and("state", networkSearchBuilder.entity().getState(), SearchCriteria.Op.IN); + networkSearchBuilder.and("removed", networkSearchBuilder.entity().getRemoved(), SearchCriteria.Op.NULL); + NetworkIdSearch = createSearchBuilder(); + NetworkIdSearch.join("network", networkSearchBuilder, networkSearchBuilder.entity().getBgpPeerId(), + NetworkIdSearch.entity().getId(), JoinBuilder.JoinType.INNER); + NetworkIdSearch.done(); + + final SearchBuilder vpcSearchBuilder = bgpPeerNetworkMapDao.createSearchBuilder(); + vpcSearchBuilder.and("vpcId", vpcSearchBuilder.entity().getVpcId(), SearchCriteria.Op.EQ); + vpcSearchBuilder.and("state", vpcSearchBuilder.entity().getState(), SearchCriteria.Op.IN); + vpcSearchBuilder.and("removed", vpcSearchBuilder.entity().getRemoved(), SearchCriteria.Op.NULL); + VpcIdSearch = createSearchBuilder(); + VpcIdSearch.join("vpc", vpcSearchBuilder, vpcSearchBuilder.entity().getBgpPeerId(), + VpcIdSearch.entity().getId(), JoinBuilder.JoinType.INNER); + VpcIdSearch.done(); + + AllFieldsSearch = createSearchBuilder(); + AllFieldsSearch.and("zoneId", AllFieldsSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("domainId", AllFieldsSearch.entity().getDomainId(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("accountId", AllFieldsSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("asNumber", AllFieldsSearch.entity().getAsNumber(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("ip4Address", AllFieldsSearch.entity().getIp4Address(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("ip6Address", AllFieldsSearch.entity().getIp6Address(), SearchCriteria.Op.EQ); + AllFieldsSearch.done(); + } + + @Override + public List listNonRevokeByNetworkId(long networkId) { + SearchCriteria sc = NetworkIdSearch.create(); + sc.setJoinParameters("network", "networkId", networkId); + sc.setJoinParameters("network", "state", BgpPeer.State.Active, BgpPeer.State.Add); + return listBy(sc); + } + + @Override + public List listNonRevokeByVpcId(long vpcId) { + SearchCriteria sc = VpcIdSearch.create(); + sc.setJoinParameters("vpc", "vpcId", vpcId); + sc.setJoinParameters("vpc", "state", BgpPeer.State.Active, BgpPeer.State.Add); + return listBy(sc); + } + + @Override + public BgpPeerVO findByZoneAndAsNumberAndAddress(long zoneId, Long asNumber, String ip4Address, String ip6Address) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters( "zoneId", zoneId); + sc.setParameters( "asNumber", asNumber); + if (ip4Address != null) { + sc.setParameters( "ip4Address", ip4Address); + } + if (ip6Address != null) { + sc.setParameters( "ip6Address", ip6Address); + } + return findOneBy(sc); + } + + @Override + public BgpPeerVO persist(BgpPeerVO bgpPeerVO, Map details) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + txn.start(); + BgpPeerVO vo = super.persist(bgpPeerVO); + + // persist the details + if (details != null && !details.isEmpty()) { + for (BgpPeer.Detail detail : details.keySet()) { + bgpPeerDetailsDao.persist(new BgpPeerDetailsVO(bgpPeerVO.getId(), detail, details.get(detail), true)); + } + } + + txn.commit(); + return vo; + } + + @Override + public List listAvailableBgpPeerIdsForAccount(long zoneId, long domainId, long accountId, boolean useSystemBgpPeers) { + if (useSystemBgpPeers) { + return listBgpPeerIdsForAccount(zoneId, domainId, accountId, false); + } else { + List dedicatedBgpPeerIds = listBgpPeerIdsForAccount(zoneId, domainId, accountId, true); + if (CollectionUtils.isNotEmpty(dedicatedBgpPeerIds)) { + return dedicatedBgpPeerIds; + } + return listBgpPeerIdsForAccount(zoneId, domainId, accountId, false); + } + } + + private List listBgpPeerIdsForAccount(long zoneId, long domainId, long accountId, boolean isDedicated) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + PreparedStatement pstmt = null; + List result = new ArrayList(); + + StringBuilder sql = isDedicated ? new StringBuilder(LIST_DEDICATED_BGP_PEERS_IDS_FOR_ACCOUNT): new StringBuilder(LIST_ALL_BGP_PEERS_IDS_FOR_ACCOUNT); + + try { + pstmt = txn.prepareAutoCloseStatement(sql.toString()); + pstmt.setLong(1, zoneId); + pstmt.setLong(2, domainId); + pstmt.setLong(3, domainId); + pstmt.setLong(4, accountId); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + result.add(rs.getLong(1)); + } + return result; + } catch (SQLException e) { + throw new CloudRuntimeException("DB Exception on: " + sql, e); + } catch (Throwable e) { + throw new CloudRuntimeException("Caught: " + sql, e); + } + } + + @Override + public int removeByAccountId(long accountId) { + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId); + return remove(sc); + } + + @Override + public int removeByDomainId(long domainId) { + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId); + return remove(sc); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerDetailsDao.java b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerDetailsDao.java new file mode 100644 index 000000000000..377bc45ebfe2 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerDetailsDao.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.network.dao; + +import org.apache.cloudstack.network.BgpPeer; +import org.apache.cloudstack.network.BgpPeerDetailsVO; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; + +import java.util.List; +import java.util.Map; + +public interface BgpPeerDetailsDao extends ResourceDetailsDao { + Map getBgpPeerDetails(long bgpPeerId); + String getDetail(long offeringId, BgpPeer.Detail detailName); + List findDomainIds(final long resourceId); + List findZoneIds(final long resourceId); + + int removeByBgpPeerId(long bgpPeerId); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerDetailsDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerDetailsDaoImpl.java new file mode 100644 index 000000000000..a974cf5e2769 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerDetailsDaoImpl.java @@ -0,0 +1,123 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.network.dao; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.cloudstack.api.ApiConstants; + +import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.SearchCriteria.Func; +import com.cloud.utils.db.SearchCriteria.Op; +import org.apache.cloudstack.network.BgpPeer; +import org.apache.cloudstack.network.BgpPeerDetailsVO; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; +import org.apache.commons.lang3.EnumUtils; + +public class BgpPeerDetailsDaoImpl extends ResourceDetailsDaoBase implements BgpPeerDetailsDao { + protected final SearchBuilder DetailSearch; + private final GenericSearchBuilder ValueSearch; + + public BgpPeerDetailsDaoImpl() { + + DetailSearch = createSearchBuilder(); + DetailSearch.and("resourceId", DetailSearch.entity().getResourceId(), SearchCriteria.Op.EQ); + DetailSearch.and("name", DetailSearch.entity().getName(), SearchCriteria.Op.EQ); + DetailSearch.and("value", DetailSearch.entity().getValue(), SearchCriteria.Op.EQ); + DetailSearch.and("display", DetailSearch.entity().isDisplay(), SearchCriteria.Op.EQ); + DetailSearch.done(); + + ValueSearch = createSearchBuilder(String.class); + ValueSearch.select(null, Func.DISTINCT, ValueSearch.entity().getValue()); + ValueSearch.and("resourceId", ValueSearch.entity().getResourceId(), SearchCriteria.Op.EQ); + ValueSearch.and("name", ValueSearch.entity().getName(), Op.EQ); + ValueSearch.and("display", ValueSearch.entity().isDisplay(), SearchCriteria.Op.EQ); + ValueSearch.done(); + } + + @Override + public Map getBgpPeerDetails(long bgpPeerId) { + SearchCriteria sc = DetailSearch.create(); + sc.setParameters("resourceId", bgpPeerId); + sc.setParameters("display", true); + + List results = search(sc, null); + if (results.size() == 0) { + return null; + } + Map details = new HashMap<>(results.size()); + for (BgpPeerDetailsVO result : results) { + details.put(result.getDetailName(), result.getValue()); + } + + return details; + } + + @Override + public String getDetail(long bgpPeerId, BgpPeer.Detail detailName) { + SearchCriteria sc = ValueSearch.create(); + sc.setParameters("name", detailName); + sc.setParameters("resourceId", bgpPeerId); + List results = customSearch(sc, null); + if (results.isEmpty()) { + return null; + } else { + return results.get(0); + } + } + + @Override + public void addDetail(long resourceId, String key, String value, boolean display) { + persist(new BgpPeerDetailsVO(resourceId, EnumUtils.getEnumIgnoreCase(BgpPeer.Detail.class, key), value, display)); + } + + @Override + public List findDomainIds(long resourceId) { + final List domainIds = new ArrayList<>(); + for (final BgpPeerDetailsVO 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 BgpPeerDetailsVO detail: findDetails(resourceId, ApiConstants.ZONE_ID)) { + final Long zoneId = Long.valueOf(detail.getValue()); + if (zoneId > 0) { + zoneIds.add(zoneId); + } + } + return zoneIds; + } + + @Override + public int removeByBgpPeerId(long bgpPeerId) { + SearchCriteria sc = DetailSearch.create(); + sc.setParameters("resourceId", bgpPeerId); + return remove(sc); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerNetworkMapDao.java b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerNetworkMapDao.java new file mode 100644 index 000000000000..8d8ec8c998a4 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerNetworkMapDao.java @@ -0,0 +1,48 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.network.dao; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.network.BgpPeerNetworkMapVO; + +import java.util.List; + + +public interface BgpPeerNetworkMapDao extends GenericDao { + + void persistForNetwork(long networkId, List bgpPeerIds); + + List listByBgpPeerId(long bgpPeerId); + + List listByNetworkId(long networkId); + + List listUsedNetworksByOtherDomains(long bgpPeerId, Long domainId); + + List listUsedNetworksByOtherAccounts(long bgpPeerId, Long accountId); + + int removeByNetworkId(long networkId); + + void persistForVpc(long vpcId, List bgpPeerIds); + + List listByVpcId(long vpcId); + + List listUsedVpcsByOtherDomains(long bgpPeerId, Long domainId); + + List listUsedVpcsByOtherAccounts(long bgpPeerId, Long accountId); + + int removeByVpcId(long vpcId); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerNetworkMapDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerNetworkMapDaoImpl.java new file mode 100644 index 000000000000..a5e5f47684a6 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/BgpPeerNetworkMapDaoImpl.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.network.dao; + +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; + +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.vpc.VpcVO; +import com.cloud.network.vpc.dao.VpcDao; +import com.cloud.utils.db.JoinBuilder; +import org.apache.cloudstack.network.BgpPeer; +import org.apache.cloudstack.network.BgpPeerNetworkMapVO; +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.utils.db.TransactionLegacy; + +@Component +public class BgpPeerNetworkMapDaoImpl extends GenericDaoBase implements BgpPeerNetworkMapDao { + + protected SearchBuilder BgpPeerNetworkVpcSearch; + protected SearchBuilder NetworkDomainAccountNeqSearch; + protected SearchBuilder VpcDomainAccountNeqSearch; + + @Inject + NetworkDao networkDao; + @Inject + VpcDao vpcDao; + + public BgpPeerNetworkMapDaoImpl() { + } + + @PostConstruct + public void init() { + BgpPeerNetworkVpcSearch = createSearchBuilder(); + BgpPeerNetworkVpcSearch.and("bgpPeerId", BgpPeerNetworkVpcSearch.entity().getBgpPeerId(), SearchCriteria.Op.EQ); + BgpPeerNetworkVpcSearch.and("networkId", BgpPeerNetworkVpcSearch.entity().getNetworkId(), SearchCriteria.Op.EQ); + BgpPeerNetworkVpcSearch.and("vpcId", BgpPeerNetworkVpcSearch.entity().getVpcId(), SearchCriteria.Op.EQ); + BgpPeerNetworkVpcSearch.done(); + + final SearchBuilder networkSearchBuilder = networkDao.createSearchBuilder(); + networkSearchBuilder.and("domainId", networkSearchBuilder.entity().getDomainId(), SearchCriteria.Op.NEQ); + networkSearchBuilder.and("accountId", networkSearchBuilder.entity().getAccountId(), SearchCriteria.Op.NEQ); + NetworkDomainAccountNeqSearch = createSearchBuilder(); + NetworkDomainAccountNeqSearch.and("bgpPeerId", NetworkDomainAccountNeqSearch.entity().getBgpPeerId(), SearchCriteria.Op.EQ); + NetworkDomainAccountNeqSearch.join("network", networkSearchBuilder, networkSearchBuilder.entity().getId(), + NetworkDomainAccountNeqSearch.entity().getNetworkId(), JoinBuilder.JoinType.INNER); + NetworkDomainAccountNeqSearch.done(); + + final SearchBuilder vpcSearchBuilder = vpcDao.createSearchBuilder(); + vpcSearchBuilder.and("domainId", vpcSearchBuilder.entity().getDomainId(), SearchCriteria.Op.NEQ); + vpcSearchBuilder.and("accountId", vpcSearchBuilder.entity().getAccountId(), SearchCriteria.Op.NEQ); + VpcDomainAccountNeqSearch = createSearchBuilder(); + VpcDomainAccountNeqSearch.and("bgpPeerId", VpcDomainAccountNeqSearch.entity().getBgpPeerId(), SearchCriteria.Op.EQ); + VpcDomainAccountNeqSearch.join("vpc", vpcSearchBuilder, vpcSearchBuilder.entity().getId(), + VpcDomainAccountNeqSearch.entity().getVpcId(), JoinBuilder.JoinType.INNER); + VpcDomainAccountNeqSearch.done(); + } + + @Override + public void persistForNetwork(long networkId, List bgpPeerIds) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + + txn.start(); + SearchCriteria sc = BgpPeerNetworkVpcSearch.create(); + sc.setParameters("networkId", networkId); + expunge(sc); + + for (Long bgpPeerId : bgpPeerIds) { + BgpPeerNetworkMapVO vo = new BgpPeerNetworkMapVO(bgpPeerId, networkId, null, BgpPeer.State.Active); + persist(vo); + } + + txn.commit(); + } + + @Override + public List listByBgpPeerId(long bgpPeerId) { + SearchCriteria sc = BgpPeerNetworkVpcSearch.create(); + sc.setParameters("bgpPeerId", bgpPeerId); + + return search(sc, null); + } + + @Override + public List listByNetworkId(long networkId) { + SearchCriteria sc = BgpPeerNetworkVpcSearch.create(); + sc.setParameters("networkId", networkId); + + return search(sc, null); + } + + @Override + public List listUsedNetworksByOtherDomains(long bgpPeerId, Long domainId) { + SearchCriteria sc = NetworkDomainAccountNeqSearch.create(); + sc.setParameters("bgpPeerId", bgpPeerId); + sc.setJoinParameters("network", "domainId", domainId); + return listBy(sc); + } + + @Override + public List listUsedNetworksByOtherAccounts(long bgpPeerId, Long accountId) { + SearchCriteria sc = NetworkDomainAccountNeqSearch.create(); + sc.setParameters("bgpPeerId", bgpPeerId); + sc.setJoinParameters("network", "accountId", accountId); + return listBy(sc); + } + + @Override + public int removeByNetworkId(long networkId) { + SearchCriteria sc = BgpPeerNetworkVpcSearch.create(); + sc.setParameters("networkId", networkId); + + return remove(sc); + } + + @Override + public void persistForVpc(long vpcId, List bgpPeerIds) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + + txn.start(); + SearchCriteria sc = BgpPeerNetworkVpcSearch.create(); + sc.setParameters("vpcId", vpcId); + expunge(sc); + + for (Long bgpPeerId : bgpPeerIds) { + BgpPeerNetworkMapVO vo = new BgpPeerNetworkMapVO(bgpPeerId, null, vpcId, BgpPeer.State.Active); + persist(vo); + } + + txn.commit(); + } + + @Override + public List listByVpcId(long vpcId) { + SearchCriteria sc = BgpPeerNetworkVpcSearch.create(); + sc.setParameters("vpcId", vpcId); + + return search(sc, null); + } + + @Override + public List listUsedVpcsByOtherDomains(long bgpPeerId, Long domainId) { + SearchCriteria sc = VpcDomainAccountNeqSearch.create(); + sc.setParameters("bgpPeerId", bgpPeerId); + sc.setJoinParameters("vpc", "domainId", domainId); + return listBy(sc); + } + + @Override + public List listUsedVpcsByOtherAccounts(long bgpPeerId, Long accountId) { + SearchCriteria sc = VpcDomainAccountNeqSearch.create(); + sc.setParameters("bgpPeerId", bgpPeerId); + sc.setJoinParameters("vpc", "accountId", accountId); + return listBy(sc); + } + + @Override + public int removeByVpcId(long vpcId) { + SearchCriteria sc = BgpPeerNetworkVpcSearch.create(); + sc.setParameters("vpcId", vpcId); + + return remove(sc); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/dao/Ipv4GuestSubnetNetworkMapDao.java b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/Ipv4GuestSubnetNetworkMapDao.java new file mode 100644 index 000000000000..c3f860009db9 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/Ipv4GuestSubnetNetworkMapDao.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.network.dao; + +import java.util.List; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap; +import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMapVO; + +public interface Ipv4GuestSubnetNetworkMapDao extends GenericDao { + List listByParent(long parentId); + List listUsedByParent(long parentId); + List listUsedByOtherDomains(long parentId, Long domainId); + List listUsedByOtherAccounts(long parentId, Long accountId); + Ipv4GuestSubnetNetworkMapVO findFirstAvailable(long parentId, long cidrSize); + Ipv4GuestSubnetNetworkMapVO findByNetworkId(long networkId); + Ipv4GuestSubnetNetworkMapVO findByVpcId(long vpcId); + Ipv4GuestSubnetNetworkMapVO findBySubnet(String subnet); + List findSubnetsInStates(Ipv4GuestSubnetNetworkMap.State... states); + void deleteByParentId(long parentId); + List listAllNoParent(); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/dao/Ipv4GuestSubnetNetworkMapDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/Ipv4GuestSubnetNetworkMapDaoImpl.java new file mode 100644 index 000000000000..95e53448907e --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/Ipv4GuestSubnetNetworkMapDaoImpl.java @@ -0,0 +1,170 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.network.dao; + +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; + +import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap; +import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMapVO; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.stereotype.Component; + +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.JoinBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@Component +@DB +public class Ipv4GuestSubnetNetworkMapDaoImpl extends GenericDaoBase implements Ipv4GuestSubnetNetworkMapDao { + + protected SearchBuilder ParentStateSearch; + protected SearchBuilder ParentIdSearch; + protected SearchBuilder NoParentSearch; + protected SearchBuilder NetworkIdSearch; + protected SearchBuilder SubnetSearch; + protected SearchBuilder StatesSearch; + protected SearchBuilder DomainAccountNeqSearch; + + @Inject + NetworkDao networkDao; + + @PostConstruct + public void init() { + ParentStateSearch = createSearchBuilder(); + ParentStateSearch.and("parentId", ParentStateSearch.entity().getParentId(), SearchCriteria.Op.EQ); + ParentStateSearch.and("state", ParentStateSearch.entity().getState(), SearchCriteria.Op.IN); + ParentStateSearch.and("subnet", ParentStateSearch.entity().getSubnet(), SearchCriteria.Op.LIKE); + ParentStateSearch.done(); + ParentIdSearch = createSearchBuilder(); + ParentIdSearch.and("parentId", ParentIdSearch.entity().getParentId(), SearchCriteria.Op.EQ); + ParentIdSearch.done(); + NoParentSearch = createSearchBuilder(); + NoParentSearch.and("parentId", NoParentSearch.entity().getParentId(), SearchCriteria.Op.NULL); + NoParentSearch.done(); + NetworkIdSearch = createSearchBuilder(); + NetworkIdSearch.and("networkId", NetworkIdSearch.entity().getNetworkId(), SearchCriteria.Op.EQ); + NetworkIdSearch.and("vpcId", NetworkIdSearch.entity().getVpcId(), SearchCriteria.Op.EQ); + NetworkIdSearch.done(); + SubnetSearch = createSearchBuilder(); + SubnetSearch.and("subnet", SubnetSearch.entity().getSubnet(), SearchCriteria.Op.EQ); + SubnetSearch.done(); + StatesSearch = createSearchBuilder(); + StatesSearch.and("state", StatesSearch.entity().getState(), SearchCriteria.Op.IN); + StatesSearch.done(); + + final SearchBuilder networkSearchBuilder = networkDao.createSearchBuilder(); + networkSearchBuilder.and("domainId", networkSearchBuilder.entity().getDomainId(), SearchCriteria.Op.NEQ); + networkSearchBuilder.and("accountId", networkSearchBuilder.entity().getAccountId(), SearchCriteria.Op.NEQ); + DomainAccountNeqSearch = createSearchBuilder(); + DomainAccountNeqSearch.and("parentId", DomainAccountNeqSearch.entity().getParentId(), SearchCriteria.Op.EQ); + DomainAccountNeqSearch.join("network", networkSearchBuilder, networkSearchBuilder.entity().getId(), + DomainAccountNeqSearch.entity().getNetworkId(), JoinBuilder.JoinType.INNER); + DomainAccountNeqSearch.done(); + } + + @Override + public List listByParent(long parentId) { + SearchCriteria sc = ParentIdSearch.create(); + sc.setParameters("parentId", parentId); + return listBy(sc, null); + } + + @Override + public List listUsedByParent(long parentId) { + SearchCriteria sc = ParentStateSearch.create(); + sc.setParameters("parentId", parentId); + sc.setParameters("state", (Object[]) new Ipv4GuestSubnetNetworkMap.State[]{Ipv4GuestSubnetNetworkMap.State.Allocated, Ipv4GuestSubnetNetworkMap.State.Allocating}); + return listBy(sc, null); + } + + @Override + public List listUsedByOtherDomains(long parentId, Long domainId) { + SearchCriteria sc = DomainAccountNeqSearch.create(); + sc.setParameters("parentId", parentId); + sc.setJoinParameters("network", "domainId", domainId); + return listBy(sc); + } + + @Override + public List listUsedByOtherAccounts(long parentId, Long accountId) { + SearchCriteria sc = DomainAccountNeqSearch.create(); + sc.setParameters("parentId", parentId); + sc.setJoinParameters("network", "accountId", accountId); + return listBy(sc); + } + + @Override + public Ipv4GuestSubnetNetworkMapVO findFirstAvailable(long parentId, long cidrSize) { + SearchCriteria sc = ParentStateSearch.create(); + sc.setParameters("parentId", parentId); + sc.setParameters("subnet", "%/" + cidrSize); + sc.setParameters("state", (Object[]) new Ipv4GuestSubnetNetworkMap.State[]{Ipv4GuestSubnetNetworkMap.State.Free}); + Filter searchFilter = new Filter(Ipv4GuestSubnetNetworkMapVO.class, "id", true, null, 1L); + List list = listBy(sc, searchFilter); + return CollectionUtils.isNotEmpty(list) ? list.get(0) : null; + } + + @Override + public Ipv4GuestSubnetNetworkMapVO findByNetworkId(long networkId) { + SearchCriteria sc = NetworkIdSearch.create(); + sc.setParameters("networkId", networkId); + return findOneBy(sc); + } + + @Override + public Ipv4GuestSubnetNetworkMapVO findByVpcId(long vpcId) { + SearchCriteria sc = NetworkIdSearch.create(); + sc.setParameters("vpcId", vpcId); + return findOneBy(sc); + } + + @Override + public Ipv4GuestSubnetNetworkMapVO findBySubnet(String subnet) { + SearchCriteria sc = SubnetSearch.create(); + sc.setParameters("subnet", subnet); + return findOneBy(sc); + } + + @Override + public List findSubnetsInStates(Ipv4GuestSubnetNetworkMap.State... states) { + SearchCriteria sc = StatesSearch.create(); + sc.setParameters("state", (Object[])states); + return listBy(sc); + } + + @Override + public void deleteByParentId(long parentId) { + SearchCriteria sc = ParentIdSearch.create(); + sc.setParameters("parentId", parentId); + remove(sc); + } + + @Override + public List listAllNoParent() { + SearchCriteria sc = NoParentSearch.create(); + return listBy(sc, null); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/dao/NetworkPermissionDao.java b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/NetworkPermissionDao.java index 1c8d1cf48ff2..e8b6322baeef 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/network/dao/NetworkPermissionDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/NetworkPermissionDao.java @@ -40,6 +40,13 @@ public interface NetworkPermissionDao extends GenericDao NetworkAndAccountSearch; private SearchBuilder NetworkIdSearch; + private SearchBuilder accountSearch; private GenericSearchBuilder FindNetworkIdsByAccount; protected NetworkPermissionDaoImpl() { @@ -45,6 +46,10 @@ protected NetworkPermissionDaoImpl() { NetworkIdSearch.and("networkId", NetworkIdSearch.entity().getNetworkId(), SearchCriteria.Op.EQ); NetworkIdSearch.done(); + accountSearch = createSearchBuilder(); + accountSearch.and("accountId", accountSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + accountSearch.done(); + FindNetworkIdsByAccount = createSearchBuilder(Long.class); FindNetworkIdsByAccount.select(null, SearchCriteria.Func.DISTINCT, FindNetworkIdsByAccount.entity().getNetworkId()); FindNetworkIdsByAccount.and("account", FindNetworkIdsByAccount.entity().getAccountId(), SearchCriteria.Op.IN); @@ -69,6 +74,16 @@ public void removeAllPermissions(long networkId) { expunge(sc); } + @Override + public void removeAccountPermissions(long accountId) { + SearchCriteria sc = accountSearch.create(); + sc.setParameters("accountId", accountId); + int networkPermissionRemoved = expunge(sc); + if (networkPermissionRemoved > 0) { + logger.debug(String.format("Removed [%s] network permission(s) for the account with Id [%s]", networkPermissionRemoved, accountId)); + } + } + @Override public NetworkPermissionVO findByNetworkAndAccount(long networkId, long accountId) { SearchCriteria sc = NetworkAndAccountSearch.create(); 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 1865b9a67834..4ce7033156fa 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 @@ -29,6 +29,7 @@ import javax.persistence.Table; import com.cloud.region.ha.GlobalLoadBalancerRule; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "global_load_balancing_rules") @@ -92,6 +93,15 @@ public GlobalLoadBalancerRuleVO(String name, String description, String gslbDoma this.state = state; } + + @Override + public String toString() { + return String.format("GlobalLoadBalancerRule %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name")); + } + + @Override public String getName() { return name; diff --git a/engine/schema/src/main/java/org/apache/cloudstack/reservation/ReservationVO.java b/engine/schema/src/main/java/org/apache/cloudstack/reservation/ReservationVO.java index e5636f0bfc99..df0ede6821ad 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/reservation/ReservationVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/reservation/ReservationVO.java @@ -18,10 +18,6 @@ // package org.apache.cloudstack.reservation; -import com.cloud.configuration.Resource; -import org.apache.cloudstack.user.ResourceReservation; -import com.cloud.utils.exception.CloudRuntimeException; - import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; @@ -29,6 +25,15 @@ import javax.persistence.Id; import javax.persistence.Table; +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.user.ResourceReservation; + +import com.cloud.configuration.Resource; +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.cloudstack.utils.identity.ManagementServerNode; + +import java.util.Date; + @Entity @Table(name = "resource_reservation") public class ReservationVO implements ResourceReservation { @@ -47,20 +52,38 @@ public class ReservationVO implements ResourceReservation { @Column(name = "resource_type", nullable = false) Resource.ResourceType resourceType; + @Column(name = "tag") + String tag; + + @Column(name = "resource_id") + Long resourceId; + @Column(name = "amount") long amount; - protected ReservationVO() - {} + @Column(name = "mgmt_server_id") + Long managementServerId; - public ReservationVO(Long accountId, Long domainId, Resource.ResourceType resourceType, Long delta) { - if (delta == null || delta <= 0) { - throw new CloudRuntimeException("resource reservations can not be made for no resources"); + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + protected ReservationVO() { + } + + public ReservationVO(Long accountId, Long domainId, Resource.ResourceType resourceType, String tag, Long delta) { + if (delta == null) { + throw new CloudRuntimeException("resource reservations can not be made for null resources"); } this.accountId = accountId; this.domainId = domainId; this.resourceType = resourceType; + this.tag = tag; this.amount = delta; + this.managementServerId = ManagementServerNode.getManagementServerId(); + } + + public ReservationVO(Long accountId, Long domainId, Resource.ResourceType resourceType, Long delta) { + this(accountId, domainId, resourceType, null, delta); } @Override @@ -83,8 +106,35 @@ public Resource.ResourceType getResourceType() { return resourceType; } + @Override + public String getTag() { + return tag; + } + @Override public Long getReservedAmount() { return amount; } + + @Override + public Long getResourceId() { + return resourceId; + } + + public void setResourceId(long resourceId) { + this.resourceId = resourceId; + } + + @Override + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public Long getManagementServerId() { + return managementServerId; + } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/reservation/dao/ReservationDao.java b/engine/schema/src/main/java/org/apache/cloudstack/reservation/dao/ReservationDao.java index eead91c7b8e9..d6d494f61f92 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/reservation/dao/ReservationDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/reservation/dao/ReservationDao.java @@ -18,11 +18,22 @@ // package org.apache.cloudstack.reservation.dao; -import com.cloud.configuration.Resource; import org.apache.cloudstack.reservation.ReservationVO; + +import com.cloud.configuration.Resource; import com.cloud.utils.db.GenericDao; +import java.util.Date; +import java.util.List; + public interface ReservationDao extends GenericDao { - long getAccountReservation(Long account, Resource.ResourceType resourceType); - long getDomainReservation(Long domain, Resource.ResourceType resourceType); + long getAccountReservation(Long account, Resource.ResourceType resourceType, String tag); + long getDomainReservation(Long domain, Resource.ResourceType resourceType, String tag); + void setResourceId(Resource.ResourceType type, Long resourceId); + List getReservationsForAccount(long accountId, Resource.ResourceType type, String tag); + void removeByIds(List reservationIds); + + int removeByMsId(long managementServerId); + + int removeStaleReservations(Long accountId, Resource.ResourceType resourceType, String tag, Date createdBefore); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/reservation/dao/ReservationDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/reservation/dao/ReservationDaoImpl.java index 6703de0b13c4..3b17f4e4294e 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/reservation/dao/ReservationDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/reservation/dao/ReservationDaoImpl.java @@ -18,39 +18,103 @@ // package org.apache.cloudstack.reservation.dao; +import java.util.Date; +import java.util.List; + +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.reservation.ReservationVO; + import com.cloud.configuration.Resource; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; -import org.apache.cloudstack.reservation.ReservationVO; - -import java.util.List; +import org.apache.cloudstack.user.ResourceReservation; +import org.apache.commons.collections.CollectionUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public class ReservationDaoImpl extends GenericDaoBase implements ReservationDao { + protected transient Logger logger = LogManager.getLogger(getClass()); private static final String RESOURCE_TYPE = "resourceType"; + private static final String RESOURCE_TAG = "resourceTag"; + private static final String RESOURCE_ID = "resourceId"; private static final String ACCOUNT_ID = "accountId"; private static final String DOMAIN_ID = "domainId"; + private static final String IDS = "ids"; + private static final String MS_ID = "managementServerId"; + private static final String CREATED = "created"; + private final SearchBuilder listResourceByAccountAndTypeSearch; private final SearchBuilder listAccountAndTypeSearch; + private final SearchBuilder listAccountAndTypeAndNoTagSearch; private final SearchBuilder listDomainAndTypeSearch; + private final SearchBuilder listDomainAndTypeAndNoTagSearch; + private final SearchBuilder listResourceByAccountAndTypeAndNoTagSearch; + private final SearchBuilder listIdsSearch; + private final SearchBuilder listMsIdSearch; public ReservationDaoImpl() { + + listResourceByAccountAndTypeSearch = createSearchBuilder(); + listResourceByAccountAndTypeSearch.and(ACCOUNT_ID, listResourceByAccountAndTypeSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + listResourceByAccountAndTypeSearch.and(RESOURCE_TYPE, listResourceByAccountAndTypeSearch.entity().getResourceType(), SearchCriteria.Op.EQ); + listResourceByAccountAndTypeSearch.and(RESOURCE_ID, listResourceByAccountAndTypeSearch.entity().getResourceId(), SearchCriteria.Op.NNULL); + listResourceByAccountAndTypeSearch.and(RESOURCE_TAG, listResourceByAccountAndTypeSearch.entity().getTag(), SearchCriteria.Op.EQ); + listResourceByAccountAndTypeSearch.done(); + + listResourceByAccountAndTypeAndNoTagSearch = createSearchBuilder(); + listResourceByAccountAndTypeAndNoTagSearch.and(ACCOUNT_ID, listResourceByAccountAndTypeAndNoTagSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + listResourceByAccountAndTypeAndNoTagSearch.and(RESOURCE_TYPE, listResourceByAccountAndTypeAndNoTagSearch.entity().getResourceType(), SearchCriteria.Op.EQ); + listResourceByAccountAndTypeAndNoTagSearch.and(RESOURCE_ID, listResourceByAccountAndTypeAndNoTagSearch.entity().getResourceId(), SearchCriteria.Op.NNULL); + listResourceByAccountAndTypeAndNoTagSearch.and(RESOURCE_TAG, listResourceByAccountAndTypeAndNoTagSearch.entity().getTag(), SearchCriteria.Op.NULL); + listResourceByAccountAndTypeAndNoTagSearch.done(); + listAccountAndTypeSearch = createSearchBuilder(); listAccountAndTypeSearch.and(ACCOUNT_ID, listAccountAndTypeSearch.entity().getAccountId(), SearchCriteria.Op.EQ); listAccountAndTypeSearch.and(RESOURCE_TYPE, listAccountAndTypeSearch.entity().getResourceType(), SearchCriteria.Op.EQ); + listAccountAndTypeSearch.and(RESOURCE_TAG, listAccountAndTypeSearch.entity().getTag(), SearchCriteria.Op.EQ); + listAccountAndTypeSearch.and(CREATED, listAccountAndTypeSearch.entity().getCreated(), SearchCriteria.Op.LT); listAccountAndTypeSearch.done(); + listAccountAndTypeAndNoTagSearch = createSearchBuilder(); + listAccountAndTypeAndNoTagSearch.and(ACCOUNT_ID, listAccountAndTypeAndNoTagSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + listAccountAndTypeAndNoTagSearch.and(RESOURCE_TYPE, listAccountAndTypeAndNoTagSearch.entity().getResourceType(), SearchCriteria.Op.EQ); + listAccountAndTypeAndNoTagSearch.and(RESOURCE_TAG, listAccountAndTypeAndNoTagSearch.entity().getTag(), SearchCriteria.Op.NULL); + listAccountAndTypeAndNoTagSearch.and(CREATED, listAccountAndTypeAndNoTagSearch.entity().getCreated(), SearchCriteria.Op.LT); + listAccountAndTypeAndNoTagSearch.done(); + listDomainAndTypeSearch = createSearchBuilder(); listDomainAndTypeSearch.and(DOMAIN_ID, listDomainAndTypeSearch.entity().getDomainId(), SearchCriteria.Op.EQ); listDomainAndTypeSearch.and(RESOURCE_TYPE, listDomainAndTypeSearch.entity().getResourceType(), SearchCriteria.Op.EQ); + listDomainAndTypeSearch.and(RESOURCE_TAG, listDomainAndTypeSearch.entity().getTag(), SearchCriteria.Op.EQ); listDomainAndTypeSearch.done(); + + listDomainAndTypeAndNoTagSearch = createSearchBuilder(); + listDomainAndTypeAndNoTagSearch.and(DOMAIN_ID, listDomainAndTypeAndNoTagSearch.entity().getDomainId(), SearchCriteria.Op.EQ); + listDomainAndTypeAndNoTagSearch.and(RESOURCE_TYPE, listDomainAndTypeAndNoTagSearch.entity().getResourceType(), SearchCriteria.Op.EQ); + listDomainAndTypeAndNoTagSearch.and(RESOURCE_TAG, listDomainAndTypeAndNoTagSearch.entity().getTag(), SearchCriteria.Op.NULL); + listDomainAndTypeAndNoTagSearch.done(); + + listIdsSearch = createSearchBuilder(); + listIdsSearch.and(IDS, listIdsSearch.entity().getId(), SearchCriteria.Op.IN); + listIdsSearch.done(); + + listMsIdSearch = createSearchBuilder(); + listMsIdSearch.and(MS_ID, listMsIdSearch.entity().getManagementServerId(), SearchCriteria.Op.EQ); + listMsIdSearch.done(); } @Override - public long getAccountReservation(Long accountId, Resource.ResourceType resourceType) { + public long getAccountReservation(Long accountId, Resource.ResourceType resourceType, String tag) { long total = 0; - SearchCriteria sc = listAccountAndTypeSearch.create(); + SearchCriteria sc; + if (tag == null) { + sc = listAccountAndTypeAndNoTagSearch.create(); + } else { + sc = listAccountAndTypeSearch.create(); + sc.setParameters(RESOURCE_TAG, tag); + } sc.setParameters(ACCOUNT_ID, accountId); sc.setParameters(RESOURCE_TYPE, resourceType); List reservations = listBy(sc); @@ -61,9 +125,15 @@ public long getAccountReservation(Long accountId, Resource.ResourceType resource } @Override - public long getDomainReservation(Long domainId, Resource.ResourceType resourceType) { + public long getDomainReservation(Long domainId, Resource.ResourceType resourceType, String tag) { long total = 0; - SearchCriteria sc = listDomainAndTypeSearch.create(); + SearchCriteria sc; + if (tag == null) { + sc = listDomainAndTypeAndNoTagSearch.create(); + } else { + sc = listDomainAndTypeSearch.create(); + sc.setParameters(RESOURCE_TAG, tag); + } sc.setParameters(DOMAIN_ID, domainId); sc.setParameters(RESOURCE_TYPE, resourceType); List reservations = listBy(sc); @@ -72,4 +142,70 @@ public long getDomainReservation(Long domainId, Resource.ResourceType resourceTy } return total; } + + @Override + public void setResourceId(Resource.ResourceType type, Long resourceId) { + Object obj = CallContext.current().getContextParameter(String.format("%s-%s", ResourceReservation.class.getSimpleName(), type.getName())); + if (obj instanceof List) { + try { + List reservationIds = (List)obj; + for (Long reservationId : reservationIds) { + ReservationVO reservation = findById(reservationId); + if (reservation != null) { + reservation.setResourceId(resourceId); + persist(reservation); + } + } + } catch (Exception e) { + logger.warn("Failed to persist reservation for resource type " + type.getName() + " for resource id " + resourceId, e); + } + } + } + + @Override + public List getReservationsForAccount(long accountId, Resource.ResourceType type, String tag) { + SearchCriteria sc; + if (tag == null) { + sc = listResourceByAccountAndTypeAndNoTagSearch.create(); + } else { + sc = listResourceByAccountAndTypeSearch.create(); + sc.setParameters(RESOURCE_TAG, tag); + } + sc.setParameters(ACCOUNT_ID, accountId); + sc.setParameters(RESOURCE_TYPE, type); + return listBy(sc); + } + + @Override + public void removeByIds(List reservationIds) { + if (CollectionUtils.isNotEmpty(reservationIds)) { + SearchCriteria sc = listIdsSearch.create(); + sc.setParameters(IDS, reservationIds.toArray()); + remove(sc); + } + } + + @Override + public int removeByMsId(long managementServerId) { + SearchCriteria sc = listMsIdSearch.create(); + sc.setParameters(MS_ID, managementServerId); + return remove(sc); + } + + @Override + public int removeStaleReservations(Long accountId, Resource.ResourceType resourceType, String tag, + Date createdBefore) { + SearchCriteria sc; + if (tag == null) { + sc = listAccountAndTypeAndNoTagSearch.create(); + } else { + sc = listAccountAndTypeSearch.create(); + sc.setParameters(RESOURCE_TAG, tag); + } + sc.setParameters(ACCOUNT_ID, accountId); + sc.setParameters(RESOURCE_TYPE, resourceType); + sc.setParameters(CREATED, createdBefore); + return remove(sc); + } + } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/DiskOfferingDetailVO.java b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/DiskOfferingDetailVO.java index f4d98c3fb7fa..7b0500680b95 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/DiskOfferingDetailVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/DiskOfferingDetailVO.java @@ -65,6 +65,10 @@ public String getName() { return name; } + public void setName(String name) { + this.name = name; + } + @Override public String getValue() { return value; diff --git a/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/FirewallRuleDetailVO.java b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/FirewallRuleDetailVO.java index 636d889fafe8..1149d0b13e77 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/FirewallRuleDetailVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/FirewallRuleDetailVO.java @@ -79,4 +79,8 @@ public long getResourceId() { public boolean isDisplay() { return display; } + + public void setValue(String value) { + this.value = value; + } } 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 5a173191be1f..1102de16e4ea 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDao.java @@ -21,6 +21,7 @@ import org.apache.cloudstack.api.ResourceDetail; +import com.cloud.utils.Pair; import com.cloud.utils.db.GenericDao; public interface ResourceDetailsDao extends GenericDao { @@ -32,6 +33,13 @@ public interface ResourceDetailsDao extends GenericDao */ R findDetail(long resourceId, String name); + /** + * Find details by key + * @param key + * @return + */ + List findDetails(String key); + /** * Find details by resourceId and key * @param resourceId @@ -53,7 +61,7 @@ public interface ResourceDetailsDao extends GenericDao * Removes all details for the resource specified * @param resourceId */ - public void removeDetails(long resourceId); + void removeDetails(long resourceId); /** @@ -76,7 +84,7 @@ public interface ResourceDetailsDao extends GenericDao * @param resourceId * @return list of details each implementing ResourceDetail interface */ - public List listDetails(long resourceId); + List listDetails(long resourceId); /** * List details for resourceId having display field = forDisplay value passed in @@ -84,17 +92,25 @@ public interface ResourceDetailsDao extends GenericDao * @param forDisplay * @return */ - public List listDetails(long resourceId, boolean forDisplay); + List listDetails(long resourceId, boolean forDisplay); - public Map listDetailsKeyPairs(long resourceId); + Map listDetailsKeyPairs(long resourceId); - public Map listDetailsKeyPairs(long resourceId, boolean forDisplay); + Map listDetailsKeyPairs(long resourceId, List keys); + + Map listDetailsKeyPairs(long resourceId, boolean forDisplay); Map listDetailsVisibility(long resourceId); - public void saveDetails(List details); + Pair, Map> listDetailsKeyPairsWithVisibility(long resourceId); + + void saveDetails(List details); + + void addDetail(long resourceId, String key, String value, boolean display); + + List findResourceIdsByNameAndValueIn(String name, Object[] values); - public void addDetail(long resourceId, String key, String value, boolean display); + long batchExpungeForResources(List ids, Long batchSize); - public List findResourceIdsByNameAndValueIn(String name, Object[] values); + String getActualValue(ResourceDetail resourceDetail); } 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 37ebfebf5ddd..eafaed182abd 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 @@ -19,17 +19,30 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; -import org.apache.cloudstack.api.ResourceDetail; +import org.apache.commons.collections.CollectionUtils; +import com.cloud.utils.Pair; +import com.cloud.utils.crypt.DBEncryptionUtil; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; -import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.db.SearchCriteria.Op; +import com.cloud.utils.db.TransactionLegacy; + +import org.apache.cloudstack.api.ResourceDetail; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.config.impl.ConfigurationVO; + +import javax.inject.Inject; public abstract class ResourceDetailsDaoBase extends GenericDaoBase implements ResourceDetailsDao { + + @Inject + private ConfigurationDao configDao; + private SearchBuilder AllFieldsSearch; public ResourceDetailsDaoBase() { @@ -52,6 +65,12 @@ public R findDetail(long resourceId, String name) { return findOneBy(sc); } + public List findDetails(String key) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("name", key); + return listBy(sc); + } + public List findDetails(long resourceId, String key) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("resourceId", resourceId); @@ -74,8 +93,7 @@ public List findDetails(String name, String value, Boolean display) { sc.setParameters("value", value); } - List results = search(sc, null); - return results; + return search(sc, null); } public Map listDetailsKeyPairs(long resourceId) { @@ -83,13 +101,27 @@ public Map listDetailsKeyPairs(long resourceId) { sc.setParameters("resourceId", resourceId); List results = search(sc, null); - Map details = new HashMap(results.size()); + Map details = new HashMap<>(results.size()); for (R result : results) { details.put(result.getName(), result.getValue()); } return details; } + @Override + public Map listDetailsKeyPairs(long resourceId, List keys) { + SearchBuilder sb = createSearchBuilder(); + sb.and("resourceId", sb.entity().getResourceId(), SearchCriteria.Op.EQ); + sb.and("name", sb.entity().getName(), SearchCriteria.Op.IN); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("resourceId", resourceId); + sc.setParameters("name", keys.toArray()); + + List results = search(sc, null); + return results.stream().collect(Collectors.toMap(R::getName, R::getValue)); + } + public Map listDetailsVisibility(long resourceId) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("resourceId", resourceId); @@ -102,12 +134,24 @@ public Map listDetailsVisibility(long resourceId) { return details; } + @Override + public Pair, Map> listDetailsKeyPairsWithVisibility(long resourceId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("resourceId", resourceId); + List results = search(sc, null); + Map> partitioned = results.stream() + .collect(Collectors.partitioningBy( + R::isDisplay, + Collectors.toMap(R::getName, R::getValue) + )); + return new Pair<>(partitioned.get(true), partitioned.get(false)); + } + public List listDetails(long resourceId) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("resourceId", resourceId); - List results = search(sc, null); - return results; + return search(sc, null); } public void removeDetails(long resourceId) { @@ -169,7 +213,7 @@ public Map listDetailsKeyPairs(long resourceId, boolean forDispl sc.setParameters("display", forDisplay); List results = search(sc, null); - Map details = new HashMap(results.size()); + Map details = new HashMap<>(results.size()); for (R result : results) { details.put(result.getName(), result.getValue()); } @@ -181,8 +225,7 @@ public List listDetails(long resourceId, boolean forDisplay) { sc.setParameters("resourceId", resourceId); sc.setParameters("display", forDisplay); - List results = search(sc, null); - return results; + return search(sc, null); } @Override @@ -201,4 +244,26 @@ public List findResourceIdsByNameAndValueIn(String name, Object[] values) return customSearch(sc, null); } + + @Override + public long batchExpungeForResources(final List ids, final Long batchSize) { + if (CollectionUtils.isEmpty(ids)) { + return 0; + } + SearchBuilder sb = createSearchBuilder(); + sb.and("ids", sb.entity().getResourceId(), Op.IN); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("ids", ids.toArray()); + return batchExpunge(sc, batchSize); + } + + @Override + public String getActualValue(ResourceDetail resourceDetail) { + ConfigurationVO configurationVO = configDao.findByName(resourceDetail.getName()); + if (configurationVO != null && configurationVO.isEncrypted()) { + return DBEncryptionUtil.decrypt(resourceDetail.getValue()); + } + return resourceDetail.getValue(); + } } 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 1b430e806e29..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 @@ -46,6 +46,10 @@ public class UserDetailVO implements ResourceDetail { private boolean display = true; 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/secstorage/HeuristicVO.java b/engine/schema/src/main/java/org/apache/cloudstack/secstorage/HeuristicVO.java index b0da0c5e7477..f647d0c83656 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/secstorage/HeuristicVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/secstorage/HeuristicVO.java @@ -120,6 +120,8 @@ public void setHeuristicRule(String heuristicRule) { @Override public String toString() { - return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "name", "heuristicRule", "type"); + return String.format("Heuristic %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "heuristicRule", "type")); } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDao.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDao.java index 1c31b3e0cc4b..eda4bcfdaa1f 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDao.java @@ -44,11 +44,13 @@ public interface ImageStoreDao extends GenericDao { List listStoresByZoneId(long zoneId); - List listAllStoresInZone(Long zoneId, String provider, DataStoreRole role); + List listAllStoresInZoneExceptId(Long zoneId, String provider, DataStoreRole role, long storeId); List findByProtocol(String protocol); ImageStoreVO findOneByZoneAndProtocol(long zoneId, String protocol); List listImageStoresByZoneIds(Long... zoneIds); + + List listByIds(List ids); } 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 a4827a1beae4..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 @@ -18,12 +18,14 @@ */ package org.apache.cloudstack.storage.datastore.db; +import java.util.Collections; import java.util.List; import java.util.Map; import javax.naming.ConfigurationException; import com.cloud.utils.db.Filter; +import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope; @@ -39,11 +41,12 @@ public class ImageStoreDaoImpl extends GenericDaoBase implem private SearchBuilder nameSearch; private SearchBuilder providerSearch; private SearchBuilder regionSearch; - private SearchBuilder storeSearch; + private SearchBuilder storesExceptIdSearch; private SearchBuilder protocolSearch; private SearchBuilder zoneProtocolSearch; private SearchBuilder zonesInSearch; + private SearchBuilder IdsSearch; public ImageStoreDaoImpl() { super(); @@ -62,6 +65,9 @@ public ImageStoreDaoImpl() { zonesInSearch.and("zonesIn", zonesInSearch.entity().getDcId(), SearchCriteria.Op.IN); zonesInSearch.done(); + IdsSearch = createSearchBuilder(); + IdsSearch.and("ids", IdsSearch.entity().getId(), SearchCriteria.Op.IN); + IdsSearch.done(); } @Override public boolean configure(String name, Map params) throws ConfigurationException { @@ -82,11 +88,12 @@ public boolean configure(String name, Map params) throws Configu regionSearch.and("role", regionSearch.entity().getRole(), SearchCriteria.Op.EQ); regionSearch.done(); - storeSearch = createSearchBuilder(); - storeSearch.and("providerName", storeSearch.entity().getProviderName(), SearchCriteria.Op.EQ); - storeSearch.and("role", storeSearch.entity().getRole(), SearchCriteria.Op.EQ); - storeSearch.and("dataCenterId", storeSearch.entity().getDcId(), SearchCriteria.Op.EQ); - storeSearch.done(); + storesExceptIdSearch = createSearchBuilder(); + storesExceptIdSearch.and("providerName", storesExceptIdSearch.entity().getProviderName(), SearchCriteria.Op.EQ); + storesExceptIdSearch.and("role", storesExceptIdSearch.entity().getRole(), SearchCriteria.Op.EQ); + storesExceptIdSearch.and("dataCenterId", storesExceptIdSearch.entity().getDcId(), SearchCriteria.Op.EQ); + storesExceptIdSearch.and("id", storesExceptIdSearch.entity().getId(), SearchCriteria.Op.NEQ); + storesExceptIdSearch.done(); return true; } @@ -107,11 +114,12 @@ public List findByProvider(String provider) { } @Override - public List listAllStoresInZone(Long zoneId, String provider, DataStoreRole role) { - SearchCriteria sc = storeSearch.create(); + public List listAllStoresInZoneExceptId(Long zoneId, String provider, DataStoreRole role, long id) { + SearchCriteria sc = storesExceptIdSearch.create(); sc.setParameters("providerName", provider); sc.setParameters("role", role); sc.setParameters("dataCenterId", zoneId); + sc.setParameters("id", id); return listBy(sc); } @@ -194,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); } @@ -206,4 +214,14 @@ public List listImageStoresByZoneIds(Long... zoneIds) { sc.setParametersIfNotNull("zonesIn", zoneIds); return listBy(sc); } + + @Override + public List listByIds(List ids) { + if (CollectionUtils.isEmpty(ids)) { + return Collections.emptyList(); + } + SearchCriteria sc = IdsSearch.create(); + sc.setParameters("ids", ids.toArray()); + return listBy(sc); + } } 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 8e5ce770f450..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 @@ -20,8 +20,17 @@ import java.util.List; import java.util.Map; +import javax.inject.Inject; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.ConfigKey.Scope; +import org.apache.cloudstack.framework.config.ScopedConfigStorage; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; import org.springframework.stereotype.Component; +import com.cloud.storage.ImageStore; +import com.cloud.utils.Pair; import com.cloud.utils.crypt.DBEncryptionUtil; import com.cloud.utils.db.QueryBuilder; import com.cloud.utils.db.SearchBuilder; @@ -29,14 +38,10 @@ import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.db.TransactionLegacy; -import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.framework.config.ConfigKey; -import org.apache.cloudstack.framework.config.ConfigKey.Scope; -import org.apache.cloudstack.framework.config.ScopedConfigStorage; -import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; - @Component public class ImageStoreDetailsDaoImpl extends ResourceDetailsDaoBase implements ImageStoreDetailsDao, ScopedConfigStorage { + @Inject + ImageStoreDao imageStoreDao; protected final SearchBuilder storeSearch; @@ -68,11 +73,11 @@ public Map getDetails(long storeId) { sc.setParameters("store", storeId); List details = listBy(sc); - Map detailsMap = new HashMap(); + Map detailsMap = new HashMap<>(); 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); @@ -105,10 +110,16 @@ public ImageStoreDetailVO findDetail(long storeId, String name) { return sc.find(); } + @Override + public String getConfigValue(long id, String key) { + ImageStoreDetailVO vo = findDetail(id, key); + return vo == null ? null : vo.getValue(); + } + @Override public String getConfigValue(long id, ConfigKey key) { ImageStoreDetailVO vo = findDetail(id, key.key()); - return vo == null ? null : vo.getValue(); + return vo == null ? null : getActualValue(vo); } @Override @@ -116,4 +127,13 @@ public void addDetail(long resourceId, String key, String value, boolean display super.addDetail(new ImageStoreDetailVO(resourceId, key, value, display)); } + @Override + public Pair getParentScope(long id) { + ImageStore store = imageStoreDao.findById(id); + if (store == null) { + return null; + } + return new Pair<>(getScope().getParent(), store.getDataCenterId()); + } + } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreVO.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreVO.java index 3ca9259c0997..c13f5aac6d69 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreVO.java @@ -33,6 +33,7 @@ import com.cloud.storage.ScopeType; import com.cloud.utils.UriUtils; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "image_store") @@ -215,4 +216,11 @@ public Long getUsedBytes() { public void setUsedBytes(Long usedBytes) { this.usedBytes = usedBytes; } + + @Override + public String toString() { + return String.format("ImageStore %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name")); + } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreDao.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreDao.java index 94f6b5ec3724..695742823ebe 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreDao.java @@ -39,4 +39,6 @@ public interface ObjectStoreDao extends GenericDao { ObjectStoreResponse setObjectStoreResponse(ObjectStoreResponse storeData, ObjectStoreVO store); Integer countAllObjectStores(); + + Boolean updateAllocatedSize(ObjectStoreVO objectStoreVO, long delta); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreDaoImpl.java index 51abde013b69..891ac0996aca 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreDaoImpl.java @@ -21,6 +21,10 @@ import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallback; +import com.cloud.utils.db.TransactionStatus; + import org.apache.cloudstack.api.response.ObjectStoreResponse; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.springframework.stereotype.Component; @@ -142,6 +146,19 @@ public ObjectStoreResponse newObjectStoreResponse(ObjectStoreVO store) { ObjectStoreResponse osResponse = new ObjectStoreResponse(); osResponse.setId(store.getUuid()); osResponse.setName(store.getName()); + if (store.getTotalSize() != null && store.getTotalSize() != 0L) { + osResponse.setStorageTotal(store.getTotalSize()); + } + if (store.getUsedSize() == null) { + osResponse.setStorageUsed(0L); + } else { + osResponse.setStorageUsed(store.getUsedSize()); + } + if (store.getAllocatedSize() == null) { + osResponse.setStorageAllocated(0L); + } else { + osResponse.setStorageAllocated(store.getAllocatedSize()); + } osResponse.setProviderName(store.getProviderName()); String url = store.getUrl(); osResponse.setUrl(url); @@ -159,4 +176,19 @@ public Integer countAllObjectStores() { SearchCriteria sc = createSearchCriteria(); return getCount(sc); } + + @Override + public Boolean updateAllocatedSize(ObjectStoreVO objectStoreVO, long delta) { + return Transaction.execute(new TransactionCallback() { + @Override + public Boolean doInTransaction(final TransactionStatus status) { + if (objectStoreVO.getAllocatedSize() != null) { + objectStoreVO.setAllocatedSize(objectStoreVO.getAllocatedSize() + delta); + } else { + objectStoreVO.setAllocatedSize(delta); + } + return update(objectStoreVO.getId(), objectStoreVO); + } + }); + } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreVO.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreVO.java index 885cbfd98ab1..23b650acc79c 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreVO.java @@ -20,6 +20,7 @@ import org.apache.cloudstack.storage.object.ObjectStore; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import javax.persistence.Column; import javax.persistence.Entity; @@ -59,8 +60,11 @@ public class ObjectStoreVO implements ObjectStore { @Column(name = "total_size") private Long totalSize; - @Column(name = "used_bytes") - private Long usedBytes; + @Column(name = "used_size") + private Long usedSize; + + @Column(name = "allocated_size") + private Long allocatedSize; @Transient Map details; @@ -129,15 +133,30 @@ public void setTotalSize(Long totalSize) { this.totalSize = totalSize; } - public Long getUsedBytes() { - return usedBytes; + public Long getUsedSize() { + return usedSize; } - public void setUsedBytes(Long usedBytes) { - this.usedBytes = usedBytes; + public void setUsedSize(Long usedSize) { + this.usedSize = usedSize; } public void setDetails(Map details) { this.details = details; } + + public Long getAllocatedSize() { + return allocatedSize; + } + + public void setAllocatedSize(Long allocatedSize) { + this.allocatedSize = allocatedSize; + } + + @Override + public String toString() { + return String.format("ObjectStore %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "providerName")); + } } 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 cfd12862c83a..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 @@ -23,6 +23,8 @@ import com.cloud.storage.ScopeType; import com.cloud.storage.Storage; import com.cloud.storage.StoragePoolStatus; +import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDao; /** @@ -40,6 +42,8 @@ public interface PrimaryDataStoreDao extends GenericDao { */ List listBy(long datacenterId, Long podId, Long clusterId, ScopeType scope); + List listBy(long datacenterId, Long podId, Long clusterId, ScopeType scope, String keyword); + /** * Set capacity of storage pool in bytes * @param id pool id. @@ -54,7 +58,9 @@ public interface PrimaryDataStoreDao extends GenericDao { */ void updateCapacityIops(long id, long capacityIops); - StoragePoolVO persist(StoragePoolVO pool, Map details, List tags, Boolean isTagARule); + StoragePoolVO persist(StoragePoolVO pool, Map details, List tags, Boolean isTagARule, List storageAccessGroups); + + StoragePoolVO persist(StoragePoolVO pool, Map details, List tags, Boolean isTagARule, boolean displayDetails, List storageAccessGroups); /** * Find pool by name. @@ -78,7 +84,9 @@ public interface PrimaryDataStoreDao extends GenericDao { */ List findPoolsByDetails(long dcId, long podId, Long clusterId, Map details, ScopeType scope); - List findPoolsByTags(long dcId, long podId, Long clusterId, String[] tags, boolean validateTagRule, long ruleExecuteTimeout); + List findPoolsByTags(long dcId, long podId, Long clusterId, ScopeType scope, String[] tags, boolean validateTagRule, long ruleExecuteTimeout); + + List findPoolsByAccessGroupsForHostConnection(Long dcId, Long podId, Long clusterId, ScopeType scope, String[] storageAccessGroups); List findDisabledPoolsByScope(long dcId, Long podId, Long clusterId, ScopeType scope); @@ -99,6 +107,8 @@ public interface PrimaryDataStoreDao extends GenericDao { void updateDetails(long poolId, Map details); + void removeDetails(long poolId); + Map getDetails(long poolId); List searchForStoragePoolTags(long poolId); @@ -115,23 +125,51 @@ public interface PrimaryDataStoreDao extends GenericDao { List findLocalStoragePoolsByTags(long dcId, long podId, Long clusterId, String[] tags, boolean validateTagRule); + List findLocalStoragePoolsByTags(long dcId, long podId, Long clusterId, String[] tags, boolean validateTagRule, String keyword); + List findZoneWideStoragePoolsByTags(long dcId, String[] tags, boolean validateTagRule); + List findZoneWideStoragePoolsByAccessGroupsForHostConnection(long dcId, String[] storageAccessGroups); + + List findZoneWideStoragePoolsByAccessGroupsAndHypervisorTypeForHostConnection(long dcId, String[] storageAccessGroups, HypervisorType type); + List findZoneWideStoragePoolsByHypervisor(long dataCenterId, HypervisorType hypervisorType); + List findZoneWideStoragePoolsByHypervisor(long dataCenterId, HypervisorType hypervisorType, String keyword); + + List findZoneWideStoragePoolsByHypervisorAndPoolType(long dataCenterId, HypervisorType hypervisorType, Storage.StoragePoolType poolType); + + List findClusterWideStoragePoolsByHypervisorAndPoolType(long clusterId, HypervisorType hypervisorType, Storage.StoragePoolType poolType); + List findLocalStoragePoolsByHostAndTags(long hostId, String[] tags); List listLocalStoragePoolByPath(long datacenterId, String path); - List findPoolsInClusters(List clusterIds); + List findPoolsInClusters(List clusterIds, String keyword); void deletePoolTags(long poolId); + void deleteStoragePoolAccessGroups(long poolId); + List listChildStoragePoolsInDatastoreCluster(long poolId); Integer countAll(); List findPoolsByStorageType(Storage.StoragePoolType storageType); + StoragePoolVO findPoolByZoneAndPath(long zoneId, String datastorePath); + List listStoragePoolsWithActiveVolumesByOfferingId(long offeringid); + + Pair, Integer> searchForIdsAndCount(Long storagePoolId, String storagePoolName, Long zoneId, + String path, Long podId, Long clusterId, Long hostId, String address, ScopeType scopeType, StoragePoolStatus status, + String keyword, String storageAccessGroup, Filter searchFilter); + + List listByIds(List ids); + + List findStoragePoolsByEmptyStorageAccessGroups(Long dcId, Long podId, Long clusterId, ScopeType scope, HypervisorType hypervisorType); + + List findPoolsByStorageTypeAndZone(Storage.StoragePoolType storageType, Long zoneId); + + 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 bcd2d27ad634..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 @@ -20,12 +20,16 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.storage.StoragePoolAndAccessGroupMapVO; +import com.cloud.storage.dao.StoragePoolAndAccessGroupMapDao; import org.apache.commons.collections.CollectionUtils; import com.cloud.host.Status; @@ -37,7 +41,9 @@ import com.cloud.storage.StoragePoolTagVO; import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.storage.dao.StoragePoolTagsDao; +import com.cloud.utils.Pair; import com.cloud.utils.db.DB; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.JoinBuilder; @@ -58,6 +64,8 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase private final SearchBuilder DcLocalStorageSearch; private final GenericSearchBuilder StatusCountSearch; private final SearchBuilder ClustersSearch; + private final SearchBuilder IdsSearch; + private final SearchBuilder DcsSearch; @Inject private StoragePoolDetailsDao _detailsDao; @@ -65,15 +73,25 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase private StoragePoolHostDao _hostDao; @Inject private StoragePoolTagsDao _tagsDao; + @Inject + StoragePoolAndAccessGroupMapDao _storagePoolAccessGroupMapDao; protected final String DetailsSqlPrefix = "SELECT storage_pool.* from storage_pool LEFT JOIN storage_pool_details ON storage_pool.id = storage_pool_details.pool_id WHERE storage_pool.removed is null and storage_pool.status = 'Up' and storage_pool.data_center_id = ? and (storage_pool.pod_id = ? or storage_pool.pod_id is null) and storage_pool.scope = ? and ("; protected final String DetailsSqlSuffix = ") GROUP BY storage_pool_details.pool_id HAVING COUNT(storage_pool_details.name) >= ?"; + protected final String DetailsForHostConnectionSqlSuffix = ") GROUP BY storage_pool_details.pool_id"; private final String ZoneWideTagsSqlPrefix = "SELECT storage_pool.* from storage_pool LEFT JOIN storage_pool_tags ON storage_pool.id = storage_pool_tags.pool_id WHERE storage_pool.removed is null and storage_pool.status = 'Up' AND storage_pool_tags.is_tag_a_rule = 0 and storage_pool.data_center_id = ? and storage_pool.scope = ? and ("; private final String ZoneWideTagsSqlSuffix = ") GROUP BY storage_pool_tags.pool_id HAVING COUNT(storage_pool_tags.tag) >= ?"; + private final String ZoneWideStorageAccessGroupsForHostConnectionSqlPrefix = "SELECT storage_pool.* from storage_pool LEFT JOIN storage_pool_and_access_group_map ON storage_pool.id = storage_pool_and_access_group_map.pool_id WHERE storage_pool.removed is null and storage_pool.status = 'Up' and storage_pool.data_center_id = ? and storage_pool.scope = ? and ("; + private final String ZoneWideStorageAccessGroupsForHostConnectionSqlSuffix = ") GROUP BY storage_pool_and_access_group_map.pool_id"; + private final String ZoneWideStorageAccessGroupsWithHypervisorTypeSqlPrefix = "SELECT storage_pool.* from storage_pool LEFT JOIN storage_pool_and_access_group_map ON storage_pool.id = storage_pool_and_access_group_map.pool_id WHERE storage_pool.removed is null and storage_pool.status = 'Up' and storage_pool.hypervisor = ? and storage_pool.data_center_id = ? and storage_pool.scope = ? and ("; + private final String ZoneWideStorageAccessGroupsWithHypervisorTypeSqlSuffix = ") GROUP BY storage_pool_and_access_group_map.pool_id"; // Storage tags are now separate from storage_pool_details, leaving only details on that table protected final String TagsSqlPrefix = "SELECT storage_pool.* from storage_pool LEFT JOIN storage_pool_tags ON storage_pool.id = storage_pool_tags.pool_id WHERE storage_pool.removed is null and storage_pool.status = 'Up' AND storage_pool_tags.is_tag_a_rule = 0 and storage_pool.data_center_id = ? and (storage_pool.pod_id = ? or storage_pool.pod_id is null) and storage_pool.scope = ? and ("; protected final String TagsSqlSuffix = ") GROUP BY storage_pool_tags.pool_id HAVING COUNT(storage_pool_tags.tag) >= ?"; + protected final String SAGsForHostConnectionSqlPrefix = "SELECT storage_pool.* from storage_pool LEFT JOIN storage_pool_and_access_group_map ON storage_pool.id = storage_pool_and_access_group_map.pool_id WHERE storage_pool.removed is null and storage_pool.status = 'Up' and storage_pool.data_center_id = ? and (storage_pool.pod_id = ? or storage_pool.pod_id is null) and storage_pool.scope = ? and ("; + + protected final String SAGsForHostConnectionSqlSuffix = ") GROUP BY storage_pool_and_access_group_map.pool_id"; private static final String GET_STORAGE_POOLS_OF_VOLUMES_WITHOUT_OR_NOT_HAVING_TAGS = "SELECT s.* " + "FROM volumes vol " + @@ -144,7 +162,15 @@ public PrimaryDataStoreDaoImpl() { ClustersSearch = createSearchBuilder(); ClustersSearch.and("clusterIds", ClustersSearch.entity().getClusterId(), Op.IN); ClustersSearch.and("status", ClustersSearch.entity().getStatus(), Op.EQ); + ClustersSearch.done(); + + IdsSearch = createSearchBuilder(); + 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 @@ -244,6 +270,11 @@ public List listLocalStoragePoolByPath(long datacenterId, String @Override public List listBy(long datacenterId, Long podId, Long clusterId, ScopeType scope) { + return listBy(datacenterId, podId, clusterId, scope, null); + } + + @Override + public List listBy(long datacenterId, Long podId, Long clusterId, ScopeType scope, String keyword) { SearchCriteria sc = null; if (clusterId != null) { sc = DcPodSearch.create(); @@ -255,6 +286,9 @@ public List listBy(long datacenterId, Long podId, Long clusterId, sc.setParameters("datacenterId", datacenterId); sc.setParameters("podId", podId); sc.setParameters("status", Status.Up); + if (keyword != null) { + sc.addAnd("name", Op.LIKE, "%" + keyword + "%"); + } if (scope != null) { sc.setParameters("scope", scope); } @@ -277,21 +311,32 @@ public StoragePoolVO listById(Integer id) { return findOneIncludingRemovedBy(sc); } + @Override + public StoragePoolVO persist(StoragePoolVO pool, Map details, List tags, Boolean isTagARule, List storageAccessGroups) { + return persist(pool, details, tags, isTagARule, true, storageAccessGroups); + } + @Override @DB - public StoragePoolVO persist(StoragePoolVO pool, Map details, List tags, Boolean isTagARule) { + public StoragePoolVO persist(StoragePoolVO pool, Map details, List tags, Boolean isTagARule, boolean displayDetails, List storageAccessGroups) { TransactionLegacy txn = TransactionLegacy.currentTxn(); txn.start(); pool = super.persist(pool); if (details != null) { for (Map.Entry detail : details.entrySet()) { - StoragePoolDetailVO vo = new StoragePoolDetailVO(pool.getId(), detail.getKey(), detail.getValue(), true); + 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); } } if (CollectionUtils.isNotEmpty(tags)) { _tagsDao.persist(pool.getId(), tags, isTagARule); } + if (CollectionUtils.isNotEmpty(storageAccessGroups)) { + _storagePoolAccessGroupMapDao.persist(pool.getId(), storageAccessGroups); + } txn.commit(); return pool; } @@ -315,6 +360,56 @@ protected List findPoolsByDetailsOrTagsInternal(long dcId, long p return searchStoragePoolsPreparedStatement(sql, dcId, podId, clusterId, scope, valuesLength); } + protected List findPoolsByDetailsOrTagsForHostConnectionInternal(long dcId, long podId, Long clusterId, ScopeType scope, String sqlValues, ValueType valuesType) { + String sqlPrefix = valuesType.equals(ValueType.DETAILS) ? DetailsSqlPrefix : SAGsForHostConnectionSqlPrefix; + String sqlSuffix = valuesType.equals(ValueType.DETAILS) ? DetailsForHostConnectionSqlSuffix : SAGsForHostConnectionSqlSuffix; + String sql = getSqlPreparedStatement(sqlPrefix, sqlSuffix, sqlValues, clusterId); + return searchStoragePoolsPreparedStatement(sql, dcId, podId, clusterId, scope, null); + } + + /** + * Search storage pools in a transaction + * @param sql prepared statement sql + * @param dcId data center id + * @param podId pod id + * @param clusterId cluster id + * @param scope scope + * @param valuesLength values length + * @return storage pools matching criteria + */ + @DB + protected List searchStoragePoolsWithHypervisorTypesPreparedStatement(String sql, HypervisorType type, long dcId, Long podId, Long clusterId, ScopeType scope, Integer valuesLength) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + List pools = new ArrayList(); + try (PreparedStatement pstmt = txn.prepareStatement(sql);) { + if (pstmt != null) { + int i = 1; + pstmt.setString(i++, type.toString()); + pstmt.setLong(i++, dcId); + if (podId != null) { + pstmt.setLong(i++, podId); + } + pstmt.setString(i++, scope.toString()); + if (clusterId != null) { + pstmt.setLong(i++, clusterId); + } + if (valuesLength != null) { + pstmt.setInt(i++, valuesLength); + } + try (ResultSet rs = pstmt.executeQuery();) { + while (rs.next()) { + pools.add(toEntityBean(rs, false)); + } + } catch (SQLException e) { + throw new CloudRuntimeException("Unable to execute :" + e.getMessage(), e); + } + } + } catch (SQLException e) { + throw new CloudRuntimeException("Unable to execute :" + e.getMessage(), e); + } + return pools; + } + /** * Search storage pools in a transaction * @param sql prepared statement sql @@ -326,7 +421,7 @@ protected List findPoolsByDetailsOrTagsInternal(long dcId, long p * @return storage pools matching criteria */ @DB - protected List searchStoragePoolsPreparedStatement(String sql, long dcId, Long podId, Long clusterId, ScopeType scope, int valuesLength) { + protected List searchStoragePoolsPreparedStatement(String sql, long dcId, Long podId, Long clusterId, ScopeType scope, Integer valuesLength) { TransactionLegacy txn = TransactionLegacy.currentTxn(); List pools = new ArrayList(); try (PreparedStatement pstmt = txn.prepareStatement(sql);) { @@ -340,7 +435,9 @@ protected List searchStoragePoolsPreparedStatement(String sql, lo if (clusterId != null) { pstmt.setLong(i++, clusterId); } - pstmt.setInt(i++, valuesLength); + if (valuesLength != null) { + pstmt.setInt(i++, valuesLength); + } try (ResultSet rs = pstmt.executeQuery();) { while (rs.next()) { pools.add(toEntityBean(rs, false)); @@ -397,6 +494,22 @@ protected String getSqlValuesFromStorageTags(String[] tags) throws NullPointerEx return sqlValues.toString(); } + /** + * Return SQL string from storage pool access group map, to be placed between SQL Prefix and SQL Suffix when creating storage tags PreparedStatement. + * @param storageAccessGroups storage tags array + * @return SQL string containing storage tag values to be placed between Prefix and Suffix when creating PreparedStatement. + * @throws NullPointerException if tags is null + * @throws IndexOutOfBoundsException if tags is not null, but empty + */ + protected String getSqlValuesFromStorageAccessGroups(String[] storageAccessGroups) throws NullPointerException, IndexOutOfBoundsException { + StringBuilder sqlValues = new StringBuilder(); + for (String tag : storageAccessGroups) { + sqlValues.append("(storage_pool_and_access_group_map.storage_access_group='").append(tag).append("') OR "); + } + sqlValues.delete(sqlValues.length() - 4, sqlValues.length()); + return sqlValues.toString(); + } + @DB @Override public List findPoolsByDetails(long dcId, long podId, Long clusterId, Map details, ScopeType scope) { @@ -405,10 +518,10 @@ public List findPoolsByDetails(long dcId, long podId, Long cluste } @Override - public List findPoolsByTags(long dcId, long podId, Long clusterId, String[] tags, boolean validateTagRule, long ruleExecuteTimeout) { + public List findPoolsByTags(long dcId, long podId, Long clusterId, ScopeType scope, String[] tags, boolean validateTagRule, long ruleExecuteTimeout) { List storagePools = null; if (tags == null || tags.length == 0) { - storagePools = listBy(dcId, podId, clusterId, ScopeType.CLUSTER); + storagePools = listBy(dcId, podId, clusterId, scope); if (validateTagRule) { storagePools = getPoolsWithoutTagRule(storagePools); @@ -416,7 +529,20 @@ public List findPoolsByTags(long dcId, long podId, Long clusterId } else { String sqlValues = getSqlValuesFromStorageTags(tags); - storagePools = findPoolsByDetailsOrTagsInternal(dcId, podId, clusterId, ScopeType.CLUSTER, sqlValues, ValueType.TAGS, tags.length); + storagePools = findPoolsByDetailsOrTagsInternal(dcId, podId, clusterId, scope, sqlValues, ValueType.TAGS, tags.length); + } + + return storagePools; + } + + @Override + public List findPoolsByAccessGroupsForHostConnection(Long dcId, Long podId, Long clusterId, ScopeType scope, String[] storageAccessGroups) { + List storagePools = null; + if (storageAccessGroups == null || storageAccessGroups.length == 0) { + storagePools = listBy(dcId, podId, clusterId, scope); + } else { + String sqlValues = getSqlValuesFromStorageAccessGroups(storageAccessGroups); + storagePools = findPoolsByDetailsOrTagsForHostConnectionInternal(dcId, podId, clusterId, scope, sqlValues, ValueType.TAGS); } return storagePools; @@ -444,9 +570,14 @@ public List findDisabledPoolsByScope(long dcId, Long podId, Long @Override public List findLocalStoragePoolsByTags(long dcId, long podId, Long clusterId, String[] tags, boolean validateTagRule) { + return findLocalStoragePoolsByTags(dcId, podId, clusterId, tags, validateTagRule, null); + } + + @Override + public List findLocalStoragePoolsByTags(long dcId, long podId, Long clusterId, String[] tags, boolean validateTagRule, String keyword) { List storagePools = null; if (tags == null || tags.length == 0) { - storagePools = listBy(dcId, podId, clusterId, ScopeType.HOST); + storagePools = listBy(dcId, podId, clusterId, ScopeType.HOST, keyword); if (validateTagRule) { storagePools = getPoolsWithoutTagRule(storagePools); @@ -528,6 +659,77 @@ protected List getPoolsWithoutTagRule(List storage return storagePoolsToReturn; } + @Override + public List findZoneWideStoragePoolsByAccessGroupsForHostConnection(long dcId, String[] storageAccessGroups) { + if (storageAccessGroups == null || storageAccessGroups.length == 0) { + QueryBuilder sc = QueryBuilder.create(StoragePoolVO.class); + sc.and(sc.entity().getDataCenterId(), Op.EQ, dcId); + sc.and(sc.entity().getStatus(), Op.EQ, Status.Up); + sc.and(sc.entity().getScope(), Op.EQ, ScopeType.ZONE); + return sc.list(); + } else { + String sqlValues = getSqlValuesFromStorageAccessGroups(storageAccessGroups); + String sql = getSqlPreparedStatement(ZoneWideStorageAccessGroupsForHostConnectionSqlPrefix, ZoneWideStorageAccessGroupsForHostConnectionSqlSuffix, sqlValues, null); + return searchStoragePoolsPreparedStatement(sql, dcId, null, null, ScopeType.ZONE, null); + } + } + + @Override + public List findZoneWideStoragePoolsByAccessGroupsAndHypervisorTypeForHostConnection(long dcId, String[] storageAccessGroups, HypervisorType type) { + if (storageAccessGroups == null || storageAccessGroups.length == 0) { + QueryBuilder sc = QueryBuilder.create(StoragePoolVO.class); + sc.and(sc.entity().getDataCenterId(), Op.EQ, dcId); + sc.and(sc.entity().getStatus(), Op.EQ, Status.Up); + sc.and(sc.entity().getScope(), Op.EQ, ScopeType.ZONE); + sc.and(sc.entity().getHypervisor(), Op.EQ, type); + return sc.list(); + } else { + String sqlValues = getSqlValuesFromStorageAccessGroups(storageAccessGroups); + String sql = getSqlPreparedStatement(ZoneWideStorageAccessGroupsWithHypervisorTypeSqlPrefix, ZoneWideStorageAccessGroupsWithHypervisorTypeSqlSuffix, sqlValues, null); + return searchStoragePoolsWithHypervisorTypesPreparedStatement(sql, type, dcId, null, null, ScopeType.ZONE, null); + } + } + + @Override + public List findStoragePoolsByEmptyStorageAccessGroups(Long dcId, Long podId, Long clusterId, ScopeType scope, HypervisorType hypervisorType) { + SearchBuilder poolSearch = createSearchBuilder(); + SearchBuilder storageAccessGroupsPoolSearch = _storagePoolAccessGroupMapDao.createSearchBuilder(); + // Set criteria for pools + poolSearch.and("scope", poolSearch.entity().getScope(), Op.EQ); + poolSearch.and("removed", poolSearch.entity().getRemoved(), Op.NULL); + poolSearch.and("status", poolSearch.entity().getStatus(), Op.EQ); + poolSearch.and("datacenterid", poolSearch.entity().getDataCenterId(), Op.EQ); + poolSearch.and("podid", poolSearch.entity().getPodId(), Op.EQ); + poolSearch.and("clusterid", poolSearch.entity().getClusterId(), Op.EQ); + poolSearch.and("hypervisortype", poolSearch.entity().getHypervisor(), Op.EQ); + + // Set StoragePoolAccessGroupMapVO.pool_id IS NULL. This ensures only pools without tags are returned + storageAccessGroupsPoolSearch.and("poolid", storageAccessGroupsPoolSearch.entity().getPoolId(), Op.NULL); + poolSearch.join("tagJoin", storageAccessGroupsPoolSearch, poolSearch.entity().getId(), storageAccessGroupsPoolSearch.entity().getPoolId(), JoinBuilder.JoinType.LEFT); + + SearchCriteria sc = poolSearch.create(); + sc.setParameters("scope", scope.toString()); + sc.setParameters("status", Status.Up.toString()); + + if (dcId != null) { + sc.setParameters("datacenterid", dcId); + } + + if (podId != null) { + sc.setParameters("podid", podId); + } + + if (clusterId != null) { + sc.setParameters("clusterid", clusterId); + } + + if (hypervisorType != null) { + sc.setParameters("hypervisortype", hypervisorType); + } + + return listBy(sc); + } + @Override public List searchForStoragePoolTags(long poolId) { return _tagsDao.getStoragePoolTags(poolId); @@ -547,6 +749,11 @@ public void updateDetails(long poolId, Map details) { } } + @Override + public void removeDetails(long poolId) { + _detailsDao.removeDetails(poolId); + } + @Override public Map getDetails(long poolId) { return _detailsDao.listDetailsKeyPairs(poolId); @@ -583,11 +790,41 @@ public List listPoolsByCluster(long clusterId) { @Override public List findZoneWideStoragePoolsByHypervisor(long dataCenterId, HypervisorType hypervisorType) { + return findZoneWideStoragePoolsByHypervisor(dataCenterId, hypervisorType, null); + } + + @Override + public List findZoneWideStoragePoolsByHypervisor(long dataCenterId, HypervisorType hypervisorType, String keyword) { QueryBuilder sc = QueryBuilder.create(StoragePoolVO.class); sc.and(sc.entity().getDataCenterId(), Op.EQ, dataCenterId); sc.and(sc.entity().getStatus(), Op.EQ, Status.Up); sc.and(sc.entity().getScope(), Op.EQ, ScopeType.ZONE); sc.and(sc.entity().getHypervisor(), Op.EQ, hypervisorType); + if (keyword != null) { + sc.and(sc.entity().getName(), Op.LIKE, "%" + keyword + "%"); + } + return sc.list(); + } + + @Override + public List findZoneWideStoragePoolsByHypervisorAndPoolType(long dataCenterId, HypervisorType hypervisorType, Storage.StoragePoolType poolType) { + QueryBuilder sc = QueryBuilder.create(StoragePoolVO.class); + sc.and(sc.entity().getDataCenterId(), Op.EQ, dataCenterId); + sc.and(sc.entity().getStatus(), Op.EQ, StoragePoolStatus.Up); + sc.and(sc.entity().getScope(), Op.EQ, ScopeType.ZONE); + sc.and(sc.entity().getHypervisor(), Op.EQ, hypervisorType); + sc.and(sc.entity().getPoolType(), Op.EQ, poolType); + return sc.list(); + } + + @Override + public List findClusterWideStoragePoolsByHypervisorAndPoolType(long clusterId, HypervisorType hypervisorType, Storage.StoragePoolType poolType) { + QueryBuilder sc = QueryBuilder.create(StoragePoolVO.class); + sc.and(sc.entity().getClusterId(), Op.EQ, clusterId); + sc.and(sc.entity().getStatus(), Op.EQ, StoragePoolStatus.Up); + sc.and(sc.entity().getScope(), Op.EQ, ScopeType.CLUSTER); + sc.and(sc.entity().getHypervisor(), Op.EQ, hypervisorType); + sc.and(sc.entity().getPoolType(), Op.EQ, poolType); return sc.list(); } @@ -596,6 +833,11 @@ public void deletePoolTags(long poolId) { _tagsDao.deleteTags(poolId); } + @Override + public void deleteStoragePoolAccessGroups(long poolId) { + _storagePoolAccessGroupMapDao.deleteStorageAccessGroups(poolId); + } + @Override public List listChildStoragePoolsInDatastoreCluster(long poolId) { QueryBuilder sc = QueryBuilder.create(StoragePoolVO.class); @@ -612,10 +854,13 @@ public Integer countAll() { } @Override - public List findPoolsInClusters(List clusterIds) { + public List findPoolsInClusters(List clusterIds, String keyword) { SearchCriteria sc = ClustersSearch.create(); sc.setParameters("clusterIds", clusterIds.toArray()); sc.setParameters("status", StoragePoolStatus.Up); + if (keyword != null) { + sc.addAnd("name", Op.LIKE, "%" + keyword + "%"); + } return listBy(sc); } @@ -626,6 +871,16 @@ public List findPoolsByStorageType(Storage.StoragePoolType storag return listBy(sc); } + @Override + public StoragePoolVO findPoolByZoneAndPath(long zoneId, String datastorePath) { + SearchCriteria sc = AllFieldSearch.create(); + sc.setParameters("datacenterId", zoneId); + if (datastorePath != null) { + sc.addAnd("path", Op.LIKE, "%/" + datastorePath); + } + return findOneBy(sc); + } + @Override public List listStoragePoolsWithActiveVolumesByOfferingId(long offeringId) { TransactionLegacy txn = TransactionLegacy.currentTxn(); @@ -646,4 +901,132 @@ public List listStoragePoolsWithActiveVolumesByOfferingId(long of throw new CloudRuntimeException("Caught: " + sql, e); } } + + @Override + public Pair, Integer> searchForIdsAndCount(Long storagePoolId, String storagePoolName, Long zoneId, + String path, Long podId, Long clusterId, Long hostId, String address, ScopeType scopeType, StoragePoolStatus status, + String keyword, String storageAccessGroup, Filter searchFilter) { + SearchCriteria sc = createStoragePoolSearchCriteria(storagePoolId, storagePoolName, zoneId, path, podId, clusterId, + hostId, address, scopeType, status, keyword, storageAccessGroup); + Pair, Integer> uniquePair = searchAndCount(sc, searchFilter); + List idList = uniquePair.first().stream().map(StoragePoolVO::getId).collect(Collectors.toList()); + return new Pair<>(idList, uniquePair.second()); + } + + @Override + public List listByIds(List ids) { + if (CollectionUtils.isEmpty(ids)) { + return Collections.emptyList(); + } + SearchCriteria sc = IdsSearch.create(); + sc.setParameters("ids", ids.toArray()); + return listBy(sc); + } + + @Override + public List findPoolsByStorageTypeAndZone(Storage.StoragePoolType storageType, Long zoneId) { + SearchCriteria sc = AllFieldSearch.create(); + sc.setParameters("poolType", storageType); + sc.addAnd("dataCenterId", Op.EQ, zoneId); + return listBy(sc); + } + + @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) { + SearchBuilder sb = createSearchBuilder(); + sb.select(null, SearchCriteria.Func.DISTINCT, sb.entity().getId()); // select distinct + // ids + sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); + sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ); + sb.and("path", sb.entity().getPath(), SearchCriteria.Op.EQ); + sb.and("dataCenterId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); + sb.and("podId", sb.entity().getPodId(), SearchCriteria.Op.EQ); + sb.and("clusterId", sb.entity().getClusterId(), SearchCriteria.Op.EQ); + sb.and("hostAddress", sb.entity().getHostAddress(), SearchCriteria.Op.EQ); + sb.and("scope", sb.entity().getScope(), SearchCriteria.Op.EQ); + sb.and("status", sb.entity().getStatus(), SearchCriteria.Op.EQ); + sb.and("parent", sb.entity().getParent(), SearchCriteria.Op.EQ); + + if (hostId != null) { + SearchBuilder hostJoin = _hostDao.createSearchBuilder(); + hostJoin.and("hostId", hostJoin.entity().getHostId(), SearchCriteria.Op.EQ); + sb.join("poolHostJoin", hostJoin, sb.entity().getId(), hostJoin.entity().getPoolId(), JoinBuilder.JoinType.INNER); + } + + if (storageAccessGroup != null) { + SearchBuilder storageAccessGroupJoin = _storagePoolAccessGroupMapDao.createSearchBuilder(); + storageAccessGroupJoin.and("storageAccessGroup", storageAccessGroupJoin.entity().getStorageAccessGroup(), SearchCriteria.Op.EQ); + sb.join("poolStorageAccessGroupJoin", storageAccessGroupJoin, sb.entity().getId(), storageAccessGroupJoin.entity().getPoolId(), JoinBuilder.JoinType.INNER); + } + + SearchCriteria sc = sb.create(); + + if (keyword != null) { + SearchCriteria ssc = createSearchCriteria(); + ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + ssc.addOr("poolType", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + + sc.addAnd("name", SearchCriteria.Op.SC, ssc); + } + + if (storagePoolId != null) { + sc.setParameters("id", storagePoolId); + } + + if (storagePoolName != null) { + sc.setParameters("name", storagePoolName); + } + + if (path != null) { + sc.setParameters("path", path); + } + if (zoneId != null) { + sc.setParameters("dataCenterId", zoneId); + } + if (podId != null) { + SearchCriteria ssc = createSearchCriteria(); + ssc.addOr("podId", SearchCriteria.Op.EQ, podId); + ssc.addOr("podId", SearchCriteria.Op.NULL); + + sc.addAnd("podId", SearchCriteria.Op.SC, ssc); + } + if (address != null) { + sc.setParameters("hostAddress", address); + } + if (clusterId != null) { + SearchCriteria ssc = createSearchCriteria(); + ssc.addOr("clusterId", SearchCriteria.Op.EQ, clusterId); + ssc.addOr("clusterId", SearchCriteria.Op.NULL); + + sc.addAnd("clusterId", SearchCriteria.Op.SC, ssc); + } + if (scopeType != null) { + sc.setParameters("scope", scopeType.toString()); + } + if (status != null) { + sc.setParameters("status", status.toString()); + } + sc.setParameters("parent", 0); + + if (hostId != null) { + sc.setJoinParameters("poolHostJoin", "hostId", hostId); + } + + if (storageAccessGroup != null) { + sc.setJoinParameters("poolStorageAccessGroupJoin", "storageAccessGroup", storageAccessGroup); + } + + return sc; + } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java index 344ff8b2a699..6aeee1ad1ccd 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java @@ -19,6 +19,7 @@ import java.util.Date; import java.util.List; +import com.cloud.hypervisor.Hypervisor; import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; @@ -46,16 +47,37 @@ public interface SnapshotDataStoreDao extends GenericDao listBySnapshot(long snapshotId, DataStoreRole role); + SnapshotDataStoreVO findParent(DataStoreRole role, Long storeId, Long zoneId, Long volumeId, boolean kvmIncrementalSnapshot, Hypervisor.HypervisorType hypervisorType); + + SnapshotDataStoreVO findBySnapshotIdAndDataStoreRoleAndState(long snapshotId, DataStoreRole role, ObjectInDataStoreStateMachine.State state); + + List listReadyByVolumeIdAndCheckpointPathNotNull(long volumeId); + + SnapshotDataStoreVO findOneBySnapshotId(long snapshotId, long zoneId); + + List listBySnapshotId(long snapshotId); + + List listBySnapshotAndDataStoreRole(long snapshotId, DataStoreRole role); + + List listExtractedSnapshotsBeforeDate(Date beforeDate); + + List listSnapshotsBySnapshotId(long snapshotId); List listReadyBySnapshot(long snapshotId, DataStoreRole role); + List listReadyBySnapshotId(long snapshotId); SnapshotDataStoreVO findBySourceSnapshot(long snapshotId, DataStoreRole role); + List findBySnapshotIdAndNotInDestroyedHiddenState(long snapshotId); + + SnapshotDataStoreVO findBySnapshotIdInAnyState(long snapshotId, DataStoreRole role); + List listDestroyed(long storeId); List findBySnapshotId(long snapshotId); + 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 @@ -106,4 +128,20 @@ 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 c095f4222e76..cdf903407c17 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDaoImpl.java @@ -16,24 +16,6 @@ // under the License. package org.apache.cloudstack.storage.datastore.db; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Map; - -import javax.inject.Inject; -import javax.naming.ConfigurationException; - -import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; -import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; -import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; -import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State; -import org.apache.commons.collections.CollectionUtils; -import org.springframework.stereotype.Component; - import com.cloud.hypervisor.Hypervisor; import com.cloud.storage.DataStoreRole; import com.cloud.storage.SnapshotVO; @@ -47,6 +29,25 @@ import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.db.UpdateBuilder; +import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State; + +import org.apache.commons.collections.CollectionUtils; + +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; + @Component public class SnapshotDataStoreDaoImpl extends GenericDaoBase implements SnapshotDataStoreDao { private static final String STORE_ID = "store_id"; @@ -58,6 +59,11 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase searchFilteringStoreIdEqStoreRoleEqStateNeqRefCntNeq; protected SearchBuilder searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq; private SearchBuilder stateSearch; @@ -67,17 +73,38 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase dataStoreAndInstallPathSearch; private SearchBuilder storeAndSnapshotIdsSearch; private SearchBuilder storeSnapshotDownloadStatusSearch; + private SearchBuilder searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqKVMCheckpointNotNull; + private SearchBuilder searchFilterStateAndDownloadUrlNotNullAndDownloadUrlCreatedBefore; + private SearchBuilder searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEq; + + private SearchBuilder searchBySnapshotId; protected static final List HYPERVISORS_SUPPORTING_SNAPSHOTS_CHAINING = List.of(Hypervisor.HypervisorType.XenServer); @Inject protected SnapshotDao snapshotDao; + @Inject + protected ImageStoreDao imageStoreDao; + private static final String FIND_OLDEST_OR_LATEST_SNAPSHOT = "select store_id, store_role, snapshot_id from cloud.snapshot_store_ref where " + " store_role = ? and volume_id = ? and state = 'Ready'" + " order by created %s " + " limit 1"; + private static final String FIND_SNAPSHOT_IN_ZONE = "SELECT ssr.* FROM " + + "snapshot_store_ref ssr, snapshots s " + + "WHERE ssr.snapshot_id=? AND ssr.snapshot_id = s.id AND s.data_center_id=?;"; + + private static final String GET_PHYSICAL_SIZE_OF_SNAPSHOTS_ON_PRIMARY_BY_ACCOUNT = "SELECT SUM(s.physical_size) " + + "FROM cloud.snapshot_store_ref s " + + "LEFT JOIN cloud.snapshots ON s.snapshot_id = snapshots.id " + + "WHERE snapshots.account_id = ? " + + "AND snapshots.removed IS NULL " + + "AND s.state = 'Ready' " + + "AND s.store_role = 'Primary' " + + "AND NOT EXISTS (SELECT 1 FROM cloud.snapshot_store_ref i WHERE i.snapshot_id = s.snapshot_id AND i.store_role = 'Image')"; + @Override public boolean configure(String name, Map params) throws ConfigurationException { super.configure(name, params); @@ -151,6 +178,31 @@ public boolean configure(String name, Map params) throws Configu storeSnapshotDownloadStatusSearch.and("downloadState", storeSnapshotDownloadStatusSearch.entity().getDownloadState(), SearchCriteria.Op.IN); storeSnapshotDownloadStatusSearch.done(); + searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqKVMCheckpointNotNull = createSearchBuilder(); + searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqKVMCheckpointNotNull.and(VOLUME_ID, searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqKVMCheckpointNotNull.entity().getVolumeId(), SearchCriteria.Op.EQ); + searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqKVMCheckpointNotNull.and(STATE, searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqKVMCheckpointNotNull.entity().getState(), SearchCriteria.Op.EQ); + searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqKVMCheckpointNotNull.and(STORE_ROLE, searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqKVMCheckpointNotNull.entity().getRole(), SearchCriteria.Op.EQ); + searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqKVMCheckpointNotNull.and(KVM_CHECKPOINT_PATH, searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqKVMCheckpointNotNull.entity().getKvmCheckpointPath(), SearchCriteria.Op.NNULL); + searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqKVMCheckpointNotNull.and(STORE_ID, searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqKVMCheckpointNotNull.entity().getDataStoreId(), SearchCriteria.Op.IN); + searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqKVMCheckpointNotNull.done(); + + searchFilterStateAndDownloadUrlNotNullAndDownloadUrlCreatedBefore = createSearchBuilder(); + searchFilterStateAndDownloadUrlNotNullAndDownloadUrlCreatedBefore.and(STATE, searchFilterStateAndDownloadUrlNotNullAndDownloadUrlCreatedBefore.entity().getState(), SearchCriteria.Op.EQ); + searchFilterStateAndDownloadUrlNotNullAndDownloadUrlCreatedBefore.and(DOWNLOAD_URL, searchFilterStateAndDownloadUrlNotNullAndDownloadUrlCreatedBefore.entity().getExtractUrl(), SearchCriteria.Op.NNULL); + 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); + + searchBySnapshotId = createSearchBuilder(); + searchBySnapshotId.and(SNAPSHOT_ID, searchBySnapshotId.entity().getSnapshotId(), SearchCriteria.Op.EQ); + searchBySnapshotId.and(STATE, searchBySnapshotId.entity().getState(), SearchCriteria.Op.EQ); + searchBySnapshotId.done(); + return true; } @@ -283,30 +335,97 @@ protected SnapshotDataStoreVO findOldestOrLatestSnapshotForVolume(long volumeId, @Override @DB public SnapshotDataStoreVO findParent(DataStoreRole role, Long storeId, Long volumeId) { - if (!isSnapshotChainingRequired(volumeId)) { + return findParent(role, storeId, null, volumeId, false, null); + } + + @Override + @DB + public SnapshotDataStoreVO findParent(DataStoreRole role, Long storeId, Long zoneId, Long volumeId, boolean kvmIncrementalSnapshot, Hypervisor.HypervisorType hypervisorType) { + if (!isSnapshotChainingRequired(volumeId, kvmIncrementalSnapshot)) { logger.trace(String.format("Snapshot chaining is not required for snapshots of volume [%s]. Returning null as parent.", volumeId)); return null; } - SearchCriteria sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create(); + SearchCriteria sc; + if (kvmIncrementalSnapshot && Hypervisor.HypervisorType.KVM.equals(hypervisorType)) { + sc = searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqKVMCheckpointNotNull.create(); + } else { + sc = searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEq.create(); + } + sc.setParameters(VOLUME_ID, volumeId); - sc.setParameters(STORE_ROLE, role.toString()); + if (role != null) { + sc.setParameters(STORE_ROLE, role.toString()); + } sc.setParameters(STATE, ObjectInDataStoreStateMachine.State.Ready.name()); - sc.setParameters(STORE_ID, storeId); + if (storeId != null) { + sc.setParameters(STORE_ID, new Long[]{storeId}); + } else if (zoneId != null) { + List imageStores = imageStoreDao.listStoresByZoneId(zoneId); + Object[] imageStoreIds = imageStores.stream().map(ImageStoreVO::getId).toArray(); + sc.setParameters(STORE_ID, imageStoreIds); + } List snapshotList = listBy(sc, new Filter(SnapshotDataStoreVO.class, CREATED, false, null, null)); - if (CollectionUtils.isNotEmpty(snapshotList)) { - return snapshotList.get(0); + if (CollectionUtils.isEmpty(snapshotList)) { + return null; + } + + SnapshotDataStoreVO parent = snapshotList.get(0); + + if (kvmIncrementalSnapshot && parent.getKvmCheckpointPath() == null && Hypervisor.HypervisorType.KVM.equals(hypervisorType)) { + return null; + } + + return parent; + } + + @Override + public SnapshotDataStoreVO findBySnapshotIdAndDataStoreRoleAndState(long snapshotId, DataStoreRole role, State state) { + SearchCriteria sc = createSearchCriteriaBySnapshotIdAndStoreRole(snapshotId, role); + sc.setParameters(STATE, state); + return findOneBy(sc); + } + + @Override + public SnapshotDataStoreVO findOneBySnapshotId(long snapshotId, long zoneId) { + try (TransactionLegacy transactionLegacy = TransactionLegacy.currentTxn()) { + try (PreparedStatement preparedStatement = transactionLegacy.prepareStatement(FIND_SNAPSHOT_IN_ZONE)) { + preparedStatement.setLong(1, snapshotId); + preparedStatement.setLong(2, zoneId); + + try (ResultSet resultSet = preparedStatement.executeQuery()) { + if (resultSet.next()) { + return toEntityBean(resultSet, false); + } + } + } + } catch (SQLException e) { + logger.warn(String.format("Failed to find %s snapshot in zone %s due to [%s].", snapshotId, zoneId, e.getMessage()), e); } return null; } @Override - public List listBySnapshot(long snapshotId, DataStoreRole role) { + public List listBySnapshotId(long snapshotId) { + SearchCriteria sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create(); + sc.setParameters(SNAPSHOT_ID, snapshotId); + return listBy(sc); + } + + @Override + public List listBySnapshotAndDataStoreRole(long snapshotId, DataStoreRole role) { SearchCriteria sc = createSearchCriteriaBySnapshotIdAndStoreRole(snapshotId, role); return listBy(sc); } + @Override + public List listSnapshotsBySnapshotId(long snapshotId) { + SearchCriteria sc = searchBySnapshotId.create(); + sc.setParameters(SNAPSHOT_ID, snapshotId); + return listBy(sc); + } + @Override public List listReadyBySnapshot(long snapshotId, DataStoreRole role) { SearchCriteria sc = createSearchCriteriaBySnapshotIdAndStoreRole(snapshotId, role); @@ -314,6 +433,14 @@ public List listReadyBySnapshot(long snapshotId, DataStoreR return listBy(sc); } + @Override + public List listReadyBySnapshotId(long snapshotId) { + SearchCriteria sc = searchBySnapshotId.create(); + sc.setParameters(SNAPSHOT_ID, snapshotId); + sc.setParameters(STATE, State.Ready); + return listBy(sc); + } + @Override public SnapshotDataStoreVO findBySourceSnapshot(long snapshotId, DataStoreRole role) { SearchCriteria sc = createSearchCriteriaBySnapshotIdAndStoreRole(snapshotId, role); @@ -321,6 +448,12 @@ public SnapshotDataStoreVO findBySourceSnapshot(long snapshotId, DataStoreRole r return findOneBy(sc); } + @Override + public SnapshotDataStoreVO findBySnapshotIdInAnyState(long snapshotId, DataStoreRole role) { + SearchCriteria sc = createSearchCriteriaBySnapshotIdAndStoreRole(snapshotId, role); + return findOneBy(sc); + } + @Override public List listAllByVolumeAndDataStore(long volumeId, DataStoreRole role) { SearchCriteria sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create(); @@ -340,9 +473,24 @@ 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 = idStateNeqSearch.create(); sc.setParameters(SNAPSHOT_ID, snapshotId); - sc.setParameters(STATE, State.Destroyed); + sc.setParameters(STATE, State.Destroyed.name()); + return listBy(sc); + } + + @Override + public List findBySnapshotIdAndNotInDestroyedHiddenState(long snapshotId) { + SearchCriteria sc = idStateNeqSearch.create(); + sc.setParameters(SNAPSHOT_ID, snapshotId); + sc.setParameters(STATE, State.Destroyed.name(), State.Hidden.name()); return listBy(sc); } @@ -485,13 +633,35 @@ protected SearchCriteria createSearchCriteriaBySnapshotIdAn return sc; } - protected boolean isSnapshotChainingRequired(long volumeId) { + protected boolean isSnapshotChainingRequired(long volumeId, boolean kvmIncrementalSnapshot) { SearchCriteria sc = snapshotVOSearch.create(); sc.setParameters(VOLUME_ID, volumeId); SnapshotVO snapshot = snapshotDao.findOneBy(sc); - return snapshot != null && HYPERVISORS_SUPPORTING_SNAPSHOTS_CHAINING.contains(snapshot.getHypervisorType()); + if (snapshot == null) { + return false; + } + + Hypervisor.HypervisorType hypervisorType = snapshot.getHypervisorType(); + return HYPERVISORS_SUPPORTING_SNAPSHOTS_CHAINING.contains(hypervisorType) || (Hypervisor.HypervisorType.KVM.equals(hypervisorType) && kvmIncrementalSnapshot); + } + + @Override + public List listReadyByVolumeIdAndCheckpointPathNotNull(long volumeId) { + SearchCriteria sc = searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqKVMCheckpointNotNull.create(); + sc.setParameters(VOLUME_ID, volumeId); + sc.setParameters(STATE, State.Ready); + return listBy(sc); + } + + @Override + public List listExtractedSnapshotsBeforeDate(Date beforeDate) { + SearchCriteria sc = searchFilterStateAndDownloadUrlNotNullAndDownloadUrlCreatedBefore.create(); + sc.setParameters(URL_CREATED_BEFORE, beforeDate); + sc.setParameters(STATE, State.Ready); + + return listBy(sc); } @Override @@ -559,4 +729,35 @@ public void updateDisplayForSnapshotStoreRole(long snapshotId, long storeId, Dat ref.setDisplay(display); update(ref.getId(), ref); } + + @Override + public int expungeBySnapshotList(final List snapshotIds, final Long batchSize) { + if (CollectionUtils.isEmpty(snapshotIds)) { + return 0; + } + SearchBuilder sb = createSearchBuilder(); + sb.and("snapshotIds", sb.entity().getSnapshotId(), SearchCriteria.Op.IN); + SearchCriteria sc = sb.create(); + 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/SnapshotDataStoreVO.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreVO.java index a1dc05fce58b..44eb7e6c02cb 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreVO.java @@ -29,6 +29,8 @@ import javax.persistence.Temporal; import javax.persistence.TemporalType; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; +import org.apache.commons.lang3.BooleanUtils; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; @@ -80,12 +82,25 @@ public class SnapshotDataStoreVO implements StateObject, List listByVolume(long volumeId, long storeId); List listByStoreIdAndInstallPaths(Long storeId, List paths); + + int expungeByVolumeList(List volumeIds, Long batchSize); } 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/SharedFSVO.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/sharedfs/SharedFSVO.java new file mode 100644 index 000000000000..8870bf6d4d89 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/sharedfs/SharedFSVO.java @@ -0,0 +1,246 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.sharedfs; + + +import java.util.Date; +import java.util.UUID; + +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; + +@Entity +@Table(name = "shared_filesystem") +public class SharedFSVO implements SharedFS { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "name") + private String name; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "description") + private String description; + + @Column(name = "domain_id") + private long domainId; + + @Column(name = "account_id") + private long accountId; + + @Column(name = "data_center_id") + private long dataCenterId; + + @Column(name = "state") + @Enumerated(value = EnumType.STRING) + private State state; + + @Column(name = "fs_provider_name") + private String fsProviderName; + + @Column(name = "protocol") + @Enumerated(value = EnumType.STRING) + private Protocol protocol; + + @Column(name = "volume_id") + private Long volumeId; + + @Column(name = "vm_id") + private Long vmId; + + @Column(name = "fs_type") + @Enumerated(value = EnumType.STRING) + private FileSystemType fsType; + + @Column(name = "service_offering_id") + private Long serviceOfferingId; + + @Column(name = "updated") + @Temporal(value = TemporalType.TIMESTAMP) + Date updated; + + @Column(name = "update_count", updatable = true, nullable = false) + protected long updatedCount; // This field should be updated everytime the + // state is updated. There's no set method in + // the vo object because it is done with in the + // dao code. + + @Column(name = GenericDao.CREATED_COLUMN) + protected Date created; + + @Column(name = GenericDao.REMOVED_COLUMN) + protected Date removed; + + public SharedFSVO() { + } + + public SharedFSVO(String name, String description, long domainId, long accountId, long dataCenterId, + String fsProviderName, Protocol protocol, FileSystemType fsType, Long serviceOfferingId) { + this.name = name; + this.description = description; + this.domainId = domainId; + this.accountId = accountId; + this.dataCenterId = dataCenterId; + this.fsProviderName = fsProviderName; + this.protocol = protocol; + this.state = State.Allocated; + this.fsType = fsType; + this.serviceOfferingId = serviceOfferingId; + this.uuid = UUID.randomUUID().toString(); + } + + @Override + public String toString() { + return String.format("SharedFS %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name")); + } + + @Override + public Class getEntityType() { + return SharedFS.class; + } + + @Override + public long getId() { + return id; + } + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(String description) { + this.description = description; + } + + @Override + public long getDomainId() { + return domainId; + } + + @Override + public long getAccountId() { + return accountId; + } + + @Override + public Long getDataCenterId() { + return dataCenterId; + } + + @Override + public State getState() { + return state; + } + + @Override + public String getFsProviderName() { + return fsProviderName; + } + + @Override + public Protocol getProtocol() { + return protocol; + } + + @Override + public Long getVolumeId() { + return volumeId; + } + + @Override + public void setVolumeId(Long volumeId) { + this.volumeId = volumeId; + } + + @Override + public Long getVmId() { + return vmId; + } + + @Override + public void setVmId(Long vmId) { + this.vmId = vmId; + } + + @Override + public FileSystemType getFsType() { + return fsType; + } + + @Override + public Long getServiceOfferingId() { + return serviceOfferingId; + } + + @Override + public void setServiceOfferingId(Long serviceOfferingId) { + this.serviceOfferingId = serviceOfferingId; + } + + @Override + public Date getUpdated() { + return updated; + } + + @Override + public long getUpdatedCount() { + return updatedCount; + } + + @Override + public void incrUpdatedCount() { + updatedCount++; + } + +} 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 new file mode 100644 index 000000000000..4735202a7623 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/sharedfs/dao/SharedFSDao.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.storage.sharedfs.dao; + +import org.apache.cloudstack.storage.sharedfs.SharedFS; +import org.apache.cloudstack.storage.sharedfs.SharedFSVO; + +import com.cloud.utils.db.GenericDao; +import com.cloud.utils.fsm.StateDao; + +import java.util.Date; +import java.util.List; + +public interface SharedFSDao extends GenericDao, StateDao { + List listSharedFSToBeDestroyed(Date date); + + SharedFSVO findSharedFSByNameAccountDomain(String name, Long accountId, Long domainId); +} 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 new file mode 100644 index 000000000000..da6220716715 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/sharedfs/dao/SharedFSDaoImpl.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.storage.sharedfs.dao; + +import com.cloud.network.dao.NetworkDao; +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.engine.cloud.entity.api.db.dao.VMNetworkMapDao; +import org.apache.cloudstack.storage.sharedfs.SharedFS; +import org.apache.cloudstack.storage.sharedfs.SharedFSVO; + +import javax.inject.Inject; +import java.util.Date; +import java.util.List; + +public class SharedFSDaoImpl extends GenericDaoBase implements SharedFSDao { + + @Inject + VMNetworkMapDao vmNetworkMapDao; + + @Inject + NetworkDao networkDao; + + protected final SearchBuilder StateUpdateCountSearch; + + protected final SearchBuilder DestroyedByTimeSearch; + + protected final SearchBuilder NameAccountDomainSearch; + + public SharedFSDaoImpl() { + StateUpdateCountSearch = createSearchBuilder(); + StateUpdateCountSearch.and("id", StateUpdateCountSearch.entity().getId(), SearchCriteria.Op.EQ); + StateUpdateCountSearch.and("state", StateUpdateCountSearch.entity().getState(), SearchCriteria.Op.EQ); + StateUpdateCountSearch.and("updatedCount", StateUpdateCountSearch.entity().getUpdatedCount(), SearchCriteria.Op.EQ); + StateUpdateCountSearch.done(); + + DestroyedByTimeSearch = createSearchBuilder(); + DestroyedByTimeSearch.and("state", DestroyedByTimeSearch.entity().getState(), SearchCriteria.Op.IN); + DestroyedByTimeSearch.and("accountId", DestroyedByTimeSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + DestroyedByTimeSearch.done(); + + NameAccountDomainSearch = createSearchBuilder(); + NameAccountDomainSearch.and("name", NameAccountDomainSearch.entity().getName(), SearchCriteria.Op.EQ); + NameAccountDomainSearch.and("accountId", NameAccountDomainSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + NameAccountDomainSearch.and("domainId", NameAccountDomainSearch.entity().getDomainId(), SearchCriteria.Op.EQ); + NameAccountDomainSearch.done(); + } + + @Override + public boolean updateState(SharedFS.State currentState, SharedFS.Event event, SharedFS.State nextState, SharedFS vo, Object data) { + + Long oldUpdated = vo.getUpdatedCount(); + Date oldUpdatedTime = vo.getUpdated(); + + SearchCriteria sc = StateUpdateCountSearch.create(); + sc.setParameters("id", vo.getId()); + sc.setParameters("state", currentState); + sc.setParameters("updatedCount", vo.getUpdatedCount()); + + vo.incrUpdatedCount(); + + UpdateBuilder builder = getUpdateBuilder(vo); + builder.set(vo, "state", nextState); + builder.set(vo, "updated", new Date()); + + int rows = update((SharedFSVO) vo, sc); + if (rows == 0 && logger.isDebugEnabled()) { + SharedFSVO dbSharedFS = findByIdIncludingRemoved(vo.getId()); + if (dbSharedFS != null) { + StringBuilder str = new StringBuilder("Unable to update ").append(vo.toString()); + str.append(": DB Data={id=").append(dbSharedFS.getId()).append("; state=").append(dbSharedFS.getState()).append("; updatecount=").append(dbSharedFS.getUpdatedCount()).append(";updatedTime=") + .append(dbSharedFS.getUpdated()); + str.append(": New Data={id=").append(vo.getId()).append("; state=").append(nextState).append("; event=").append(event).append("; updatecount=").append(vo.getUpdatedCount()) + .append("; updatedTime=").append(vo.getUpdated()); + str.append(": stale Data={id=").append(vo.getId()).append("; state=").append(currentState).append("; event=").append(event).append("; updatecount=").append(oldUpdated) + .append("; updatedTime=").append(oldUpdatedTime); + } else { + logger.debug("Unable to update sharedfs: id=" + vo.getId() + ", as it is not present in the database anymore"); + } + } + return rows > 0; + } + + @Override + public List listSharedFSToBeDestroyed(Date date) { + SearchCriteria sc = DestroyedByTimeSearch.create(); + sc.setParameters("state", SharedFS.State.Destroyed, SharedFS.State.Expunging, SharedFS.State.Error); + sc.setParameters("updateTime", date); + return listBy(sc); + } + + @Override + public SharedFSVO findSharedFSByNameAccountDomain(String name, Long accountId, Long domainId) { + SearchCriteria sc = NameAccountDomainSearch.create(); + sc.setParameters("name", name); + sc.setParameters("accountId", accountId); + sc.setParameters("domainId", domainId); + return findOneBy(sc); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/util/CPUArchConverter.java b/engine/schema/src/main/java/org/apache/cloudstack/util/CPUArchConverter.java new file mode 100644 index 000000000000..8e56cce739d8 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/util/CPUArchConverter.java @@ -0,0 +1,36 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.util; + +import com.cloud.cpu.CPU; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; + +@Converter(autoApply = true) +public class CPUArchConverter implements AttributeConverter { + + @Override + public String convertToDatabaseColumn(CPU.CPUArch cpuArch) { + return cpuArch == null ? CPU.CPUArch.amd64.getType() : cpuArch.getType(); + } + + @Override + public CPU.CPUArch convertToEntityAttribute(String attribute) { + return CPU.CPUArch.fromType(attribute); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/util/HypervisorTypeConverter.java b/engine/schema/src/main/java/org/apache/cloudstack/util/HypervisorTypeConverter.java new file mode 100644 index 000000000000..57c12a9b7faa --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/util/HypervisorTypeConverter.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 +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.util; + +import com.cloud.hypervisor.Hypervisor; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; + +/** + * Converts {@link com.cloud.hypervisor.Hypervisor.HypervisorType} to and from {@link String} using {@link com.cloud.hypervisor.Hypervisor.HypervisorType#name()}. + */ +@Converter +public class HypervisorTypeConverter implements AttributeConverter { + @Override + public String convertToDatabaseColumn(Hypervisor.HypervisorType attribute) { + return attribute != null ? attribute.name() : null; + } + + @Override + public Hypervisor.HypervisorType convertToEntityAttribute(String dbData) { + return dbData != null ? Hypervisor.HypervisorType.valueOf(dbData) : null; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduleVO.java b/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduleVO.java index 176f88c5f6ba..e0065db1e77a 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduleVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduleVO.java @@ -19,6 +19,7 @@ package org.apache.cloudstack.vm.schedule; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import javax.persistence.Column; import javax.persistence.Entity; @@ -95,6 +96,11 @@ public VMScheduleVO(long vmId, String description, String schedule, String timeZ this.enabled = enabled; } + @Override + public String toString() { + return String.format("VMSchedule %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "uuid", "action", "description")); + } + @Override public String getUuid() { return uuid; diff --git a/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduledJobVO.java b/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduledJobVO.java index 0c2dd94cce58..775e9cfe40cf 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduledJobVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/VMScheduledJobVO.java @@ -18,6 +18,8 @@ */ package org.apache.cloudstack.vm.schedule; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; + import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EnumType; @@ -71,6 +73,14 @@ public VMScheduledJobVO(long vmId, long vmScheduleId, VMSchedule.Action action, this.scheduledTime = scheduledTime; } + + @Override + public String toString() { + return String.format("VMScheduledJob %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "action", "vmScheduleId", "vmId", "asyncJobId")); + } + @Override public String getUuid() { return uuid; diff --git a/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/dao/VMScheduledJobDao.java b/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/dao/VMScheduledJobDao.java index 7b8c01aae6ad..835ac696f26c 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/dao/VMScheduledJobDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/vm/schedule/dao/VMScheduledJobDao.java @@ -31,4 +31,6 @@ public interface VMScheduledJobDao extends GenericDao { int expungeJobsForSchedules(List scheduleId, Date dateAfter); int expungeJobsBefore(Date currentTimestamp); + + VMScheduledJobVO findByScheduleAndTimestamp(long scheduleId, Date scheduledTimestamp); } 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 index 50a2b12fd774..2f08a41b92e4 100644 --- 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 @@ -39,6 +39,8 @@ public class VMScheduledJobDaoImpl extends GenericDaoBase expungeJobForScheduleSearch; + private final SearchBuilder scheduleAndTimestampSearch; + static final String SCHEDULED_TIMESTAMP = "scheduled_timestamp"; static final String VM_SCHEDULE_ID = "vm_schedule_id"; @@ -58,6 +60,11 @@ public VMScheduledJobDaoImpl() { expungeJobForScheduleSearch.and(VM_SCHEDULE_ID, expungeJobForScheduleSearch.entity().getVmScheduleId(), SearchCriteria.Op.IN); expungeJobForScheduleSearch.and(SCHEDULED_TIMESTAMP, expungeJobForScheduleSearch.entity().getScheduledTime(), SearchCriteria.Op.GTEQ); expungeJobForScheduleSearch.done(); + + scheduleAndTimestampSearch = createSearchBuilder(); + scheduleAndTimestampSearch.and(VM_SCHEDULE_ID, scheduleAndTimestampSearch.entity().getVmScheduleId(), SearchCriteria.Op.EQ); + scheduleAndTimestampSearch.and(SCHEDULED_TIMESTAMP, scheduleAndTimestampSearch.entity().getScheduledTime(), SearchCriteria.Op.EQ); + scheduleAndTimestampSearch.done(); } /** @@ -92,4 +99,12 @@ public int expungeJobsBefore(Date date) { sc.setParameters(SCHEDULED_TIMESTAMP, date); return expunge(sc); } + + @Override + public VMScheduledJobVO findByScheduleAndTimestamp(long scheduleId, Date scheduledTimestamp) { + SearchCriteria sc = scheduleAndTimestampSearch.create(); + sc.setParameters(VM_SCHEDULE_ID, scheduleId); + sc.setParameters(SCHEDULED_TIMESTAMP, scheduledTimestamp); + return findOneBy(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 0c46c5ff9341..1846c3c62a0e 100644 --- a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-common-daos-between-management-and-usage-context.xml +++ b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-common-daos-between-management-and-usage-context.xml @@ -62,11 +62,16 @@ - + + + + + + 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 5d9583831610..edc14d9fa0cc 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 @@ -117,6 +117,7 @@ + @@ -136,6 +137,8 @@ + + @@ -157,7 +160,6 @@ - @@ -187,7 +189,6 @@ - @@ -197,12 +198,14 @@ + + @@ -268,6 +271,8 @@ + + @@ -288,4 +293,23 @@ + + + + + + + + + + + + + + + + + + + diff --git a/engine/schema/src/main/resources/META-INF/db/data-217to218.sql b/engine/schema/src/main/resources/META-INF/db/data-217to218.sql index 5c1253143f45..1a03e9b79981 100755 --- a/engine/schema/src/main/resources/META-INF/db/data-217to218.sql +++ b/engine/schema/src/main/resources/META-INF/db/data-217to218.sql @@ -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/engine/schema/src/main/resources/META-INF/db/procedures/cloud.add_guest_os_and_hypervisor_mapping.sql b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.add_guest_os_and_hypervisor_mapping.sql new file mode 100644 index 000000000000..efe56bccf2d3 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.add_guest_os_and_hypervisor_mapping.sql @@ -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. + +-- PR#4699 Drop the procedure `ADD_GUEST_OS_AND_HYPERVISOR_MAPPING` if it already exist. +DROP PROCEDURE IF EXISTS `cloud`.`ADD_GUEST_OS_AND_HYPERVISOR_MAPPING`; + +-- PR#4699 Create the procedure `ADD_GUEST_OS_AND_HYPERVISOR_MAPPING` to add guest_os and guest_os_hypervisor mapping. +CREATE PROCEDURE `cloud`.`ADD_GUEST_OS_AND_HYPERVISOR_MAPPING` ( + IN guest_os_category_id bigint(20) unsigned, + IN guest_os_display_name VARCHAR(255), + IN guest_os_hypervisor_hypervisor_type VARCHAR(32), + IN guest_os_hypervisor_hypervisor_version VARCHAR(32), + IN guest_os_hypervisor_guest_os_name VARCHAR(255) + ) +BEGIN +INSERT INTO cloud.guest_os (uuid, category_id, display_name, created) +SELECT UUID(), guest_os_category_id, guest_os_display_name, now() +FROM DUAL +WHERE not exists( SELECT 1 + FROM cloud.guest_os + WHERE cloud.guest_os.category_id = guest_os_category_id + AND cloud.guest_os.display_name = guest_os_display_name) + +; INSERT INTO cloud.guest_os_hypervisor (uuid, hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created) + SELECT UUID(), guest_os_hypervisor_hypervisor_type, guest_os_hypervisor_hypervisor_version, guest_os_hypervisor_guest_os_name, guest_os.id, now() + FROM cloud.guest_os + WHERE guest_os.category_id = guest_os_category_id + AND guest_os.display_name = guest_os_display_name + AND NOT EXISTS (SELECT 1 + FROM cloud.guest_os_hypervisor as hypervisor + WHERE hypervisor_type = guest_os_hypervisor_hypervisor_type + AND hypervisor_version = guest_os_hypervisor_hypervisor_version + AND hypervisor.guest_os_id = guest_os.id + AND hypervisor.guest_os_name = guest_os_hypervisor_guest_os_name) +;END; diff --git a/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_add_column.sql b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_add_column.sql new file mode 100644 index 000000000000..7872f60b2dbb --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_add_column.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. + +-- in cloud +DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_ADD_COLUMN`; +CREATE PROCEDURE `cloud`.`IDEMPOTENT_ADD_COLUMN` ( + IN in_table_name VARCHAR(200), + IN in_column_name VARCHAR(200), + IN in_column_definition VARCHAR(1000) +) +BEGIN + + DECLARE CONTINUE HANDLER FOR 1060 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', 'ADD COLUMN') ; SET @ddl = CONCAT(@ddl, ' ', in_column_name); SET @ddl = CONCAT(@ddl, ' ', in_column_definition); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; diff --git a/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_add_foreign_key.sql b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_add_foreign_key.sql new file mode 100644 index 000000000000..754c02acb93f --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_add_foreign_key.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. + +DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_ADD_FOREIGN_KEY`; + +CREATE PROCEDURE `cloud`.`IDEMPOTENT_ADD_FOREIGN_KEY` ( + IN in_table_name VARCHAR(200) + , IN in_key_name VARCHAR(200) + , IN in_foreign_key VARCHAR(200) + , IN in_references VARCHAR(1000) +) +BEGIN + + DECLARE CONTINUE HANDLER FOR 1061 BEGIN END; SET @ddl = CONCAT_WS(' ', 'ALTER TABLE ', in_table_name, ' ADD CONSTRAINT ', in_key_name, ' FOREIGN KEY ', in_foreign_key, ' REFERENCES ', in_references, ' ON DELETE CASCADE'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; diff --git a/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_add_key.sql b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_add_key.sql new file mode 100644 index 000000000000..8083080088ea --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_add_key.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. + +DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_ADD_KEY`; + +CREATE PROCEDURE `cloud`.`IDEMPOTENT_ADD_KEY` ( + IN in_index_name VARCHAR(200) + , IN in_table_name VARCHAR(200) + , IN in_key_definition VARCHAR(1000) +) +BEGIN + + DECLARE CONTINUE HANDLER FOR 1061 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', ' ADD KEY ') ; SET @ddl = CONCAT(@ddl, ' ', in_index_name); SET @ddl = CONCAT(@ddl, ' ', in_key_definition); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; diff --git a/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_add_unique_index.sql b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_add_unique_index.sql new file mode 100644 index 000000000000..22f490ad0fa1 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_add_unique_index.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. + +-- Idempotent ADD UNIQUE INDEX +DROP PROCEDURE IF EXISTS `cloud_usage`.`IDEMPOTENT_ADD_UNIQUE_INDEX`; +CREATE PROCEDURE `cloud_usage`.`IDEMPOTENT_ADD_UNIQUE_INDEX` ( + IN in_table_name VARCHAR(200) +, IN in_index_name VARCHAR(200) +, IN in_index_definition VARCHAR(1000) +) +BEGIN + DECLARE CONTINUE HANDLER FOR 1061 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', 'ADD UNIQUE INDEX ', in_index_name); SET @ddl = CONCAT(@ddl, ' ', in_index_definition); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; diff --git a/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_add_unique_key.sql b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_add_unique_key.sql new file mode 100644 index 000000000000..5d4cbf6c7709 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_add_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. + +-- Idempotent ADD UNIQUE KEY +DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_ADD_UNIQUE_KEY`; +CREATE PROCEDURE `cloud`.`IDEMPOTENT_ADD_UNIQUE_KEY` ( + IN in_table_name VARCHAR(200) +, IN in_key_name VARCHAR(200) +, IN in_key_definition VARCHAR(1000) +) +BEGIN + DECLARE CONTINUE HANDLER FOR 1061 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', 'ADD UNIQUE KEY ', in_key_name); SET @ddl = CONCAT(@ddl, ' ', in_key_definition); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; diff --git a/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_change_column.sql b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_change_column.sql new file mode 100644 index 000000000000..d63c92e6b81b --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_change_column.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. + +-- in usage Idempotent CHANGE COLUMN +DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_CHANGE_COLUMN`; +CREATE PROCEDURE `cloud`.`IDEMPOTENT_CHANGE_COLUMN` ( + IN in_table_name VARCHAR(200) + , IN in_column_name VARCHAR(200) + , IN in_column_new_name VARCHAR(200) + , IN in_column_new_definition VARCHAR(1000) +) +BEGIN + DECLARE CONTINUE HANDLER FOR 1054 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', 'CHANGE COLUMN') ; SET @ddl = CONCAT(@ddl, ' ', in_column_name); SET @ddl = CONCAT(@ddl, ' ', in_column_new_name); SET @ddl = CONCAT(@ddl, ' ', in_column_new_definition); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; diff --git a/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_create_unique_index.sql b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_create_unique_index.sql new file mode 100644 index 000000000000..167b17412f13 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_create_unique_index.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. + +DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_CREATE_UNIQUE_INDEX`; + +CREATE PROCEDURE `cloud`.`IDEMPOTENT_CREATE_UNIQUE_INDEX` ( + IN in_index_name VARCHAR(200) + , IN in_table_name VARCHAR(200) + , IN in_index_definition VARCHAR(1000) +) +BEGIN + + DECLARE CONTINUE HANDLER FOR 1061 BEGIN END; SET @ddl = CONCAT('CREATE UNIQUE INDEX ', in_index_name); SET @ddl = CONCAT(@ddl, ' ', ' ON ') ; SET @ddl = CONCAT(@ddl, ' ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', in_index_definition); 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_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_foreign_key.sql b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_drop_foreign_key.sql new file mode 100644 index 000000000000..0ba0a411ca96 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_drop_foreign_key.sql @@ -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. + +DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_DROP_FOREIGN_KEY`; + +CREATE PROCEDURE `cloud`.`IDEMPOTENT_DROP_FOREIGN_KEY` ( + IN in_table_name VARCHAR(200), + IN in_foreign_key_name VARCHAR(200) +) +BEGIN + DECLARE CONTINUE HANDLER FOR 1091, 1025 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', ' DROP FOREIGN KEY '); SET @ddl = CONCAT(@ddl, ' ', in_foreign_key_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_index.sql b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_drop_index.sql new file mode 100644 index 000000000000..1e1afd265970 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_drop_index.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. + +-- in cloud +DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_DROP_INDEX`; + +CREATE PROCEDURE `cloud`.`IDEMPOTENT_DROP_INDEX` ( + IN in_index_name VARCHAR(200) + , IN in_table_name VARCHAR(200) +) +BEGIN + + DECLARE CONTINUE HANDLER FOR 1091 BEGIN END; SET @ddl = CONCAT('DROP INDEX ', in_index_name); SET @ddl = CONCAT(@ddl, ' ', ' ON ') ; SET @ddl = CONCAT(@ddl, ' ', in_table_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.idempotent_insert_guestos_hypervisor_mapping.sql b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_insert_guestos_hypervisor_mapping.sql new file mode 100644 index 000000000000..7af96f56145c --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_insert_guestos_hypervisor_mapping.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. + +DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_INSERT_GUESTOS_HYPERVISOR_MAPPING`; + +CREATE PROCEDURE `cloud`.`IDEMPOTENT_INSERT_GUESTOS_HYPERVISOR_MAPPING`( + IN in_hypervisor_type VARCHAR(32), + IN in_hypervisor_version VARCHAR(32), + IN in_guest_os_name VARCHAR(255), + IN in_guest_os_id BIGINT(20) UNSIGNED, + IN is_user_defined int(1) UNSIGNED) +BEGIN + IF NOT EXISTS ((SELECT * FROM `cloud`.`guest_os_hypervisor` WHERE + hypervisor_type=in_hypervisor_type AND + hypervisor_version=in_hypervisor_version AND + guest_os_id = in_guest_os_id)) + THEN + INSERT INTO `cloud`.`guest_os_hypervisor` ( + uuid, + hypervisor_type, + hypervisor_version, + guest_os_name, + guest_os_id, + created, + is_user_defined) + VALUES ( + UUID(), + in_hypervisor_type, + in_hypervisor_version, + in_guest_os_name, + in_guest_os_id, + utc_timestamp(), + is_user_defined + ); END IF; END;; diff --git a/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_update_api_permission.sql b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_update_api_permission.sql new file mode 100644 index 000000000000..c53e00670612 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_update_api_permission.sql @@ -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. + +DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`; + +CREATE PROCEDURE `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION` ( + IN role VARCHAR(255), + IN rule VARCHAR(255), + IN permission VARCHAR(255) +) +BEGIN + DECLARE role_id BIGINT(20) UNSIGNED +; DECLARE max_sort_order BIGINT(20) UNSIGNED + +; SELECT `r`.`id` INTO role_id + FROM `cloud`.`roles` `r` + WHERE `r`.`name` = role + AND `r`.`is_default` = 1 + +; SELECT MAX(`rp`.`sort_order`) INTO max_sort_order + FROM `cloud`.`role_permissions` `rp` + WHERE `rp`.`role_id` = role_id + +; IF NOT EXISTS ( + SELECT * FROM `cloud`.`role_permissions` `rp` + WHERE `rp`.`role_id` = role_id + AND `rp`.`rule` = rule + ) THEN + UPDATE `cloud`.`role_permissions` `rp` + SET `rp`.`sort_order` = max_sort_order + 1 + WHERE `rp`.`sort_order` = max_sort_order + AND `rp`.`role_id` = role_id + +; INSERT INTO `cloud`.`role_permissions` + (uuid, role_id, rule, permission, sort_order) + VALUES (uuid(), role_id, rule, permission, max_sort_order) +; END IF +;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/procedures/usage.idempotent_add_column.sql b/engine/schema/src/main/resources/META-INF/db/procedures/usage.idempotent_add_column.sql new file mode 100644 index 000000000000..e257b64bd8a3 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/procedures/usage.idempotent_add_column.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 usage +DROP PROCEDURE IF EXISTS `cloud_usage`.`IDEMPOTENT_ADD_COLUMN`; +CREATE PROCEDURE `cloud_usage`.`IDEMPOTENT_ADD_COLUMN` ( + IN in_table_name VARCHAR(200) +, IN in_column_name VARCHAR(200) +, IN in_column_definition VARCHAR(1000) +) +BEGIN + DECLARE CONTINUE HANDLER FOR 1060 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', 'ADD COLUMN') ; SET @ddl = CONCAT(@ddl, ' ', in_column_name); SET @ddl = CONCAT(@ddl, ' ', in_column_definition); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; diff --git a/engine/schema/src/main/resources/META-INF/db/procedures/usage.idempotent_change_column.sql b/engine/schema/src/main/resources/META-INF/db/procedures/usage.idempotent_change_column.sql new file mode 100644 index 000000000000..a47b6a1fbf9b --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/procedures/usage.idempotent_change_column.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. + +-- in usage Idempotent CHANGE COLUMN +DROP PROCEDURE IF EXISTS `cloud_usage`.`IDEMPOTENT_CHANGE_COLUMN`; +CREATE PROCEDURE `cloud_usage`.`IDEMPOTENT_CHANGE_COLUMN` ( + IN in_table_name VARCHAR(200) + , IN in_old_column_name VARCHAR(200) + , IN in_new_column_name VARCHAR(200) + , IN in_column_definition VARCHAR(1000) +) +BEGIN + DECLARE CONTINUE HANDLER FOR 1060 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', ' CHANGE COLUMN') ; SET @ddl = CONCAT(@ddl, ' ', in_old_column_name); SET @ddl = CONCAT(@ddl, ' ', in_new_column_name); SET @ddl = CONCAT(@ddl, ' ', in_column_definition); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; diff --git a/engine/schema/src/main/resources/META-INF/db/procedures/usage.idempotent_drop_index.sql b/engine/schema/src/main/resources/META-INF/db/procedures/usage.idempotent_drop_index.sql new file mode 100644 index 000000000000..f824ebad98e1 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/procedures/usage.idempotent_drop_index.sql @@ -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. + +-- in usage +DROP PROCEDURE IF EXISTS `cloud_usage`.`IDEMPOTENT_DROP_INDEX`; +CREATE PROCEDURE `cloud_usage`.`IDEMPOTENT_DROP_INDEX` ( + IN in_index_name VARCHAR(200) +, IN in_table_name VARCHAR(200) +) +BEGIN + DECLARE CONTINUE HANDLER FOR 1091 BEGIN END; SET @ddl = CONCAT('DROP INDEX ', in_index_name); SET @ddl = CONCAT(@ddl, ' ', ' ON ') ; SET @ddl = CONCAT(@ddl, ' ', in_table_name); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-20to21.sql b/engine/schema/src/main/resources/META-INF/db/schema-20to21.sql index 7013046ca439..000d0f077cc3 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-20to21.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-20to21.sql @@ -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 @@ -141,14 +141,14 @@ ALTER TABLE `cloud`.`host` ADD COLUMN `cluster_id` bigint unsigned; -- -- enforced in postporcess-20to21.sql -ALTER TABLE `cloud`.`host_pod_ref` ADD COLUMN `gateway` varchar(255); -- need to migrage data with user input +ALTER TABLE `cloud`.`host_pod_ref` ADD COLUMN `gateway` varchar(255); -- need to migrage data with user input -ALTER TABLE `cloud`.`service_offering` ADD COLUMN `recreatable` tinyint(1) unsigned NOT NULL DEFAULT 0; +ALTER TABLE `cloud`.`service_offering` ADD COLUMN `recreatable` tinyint(1) unsigned NOT NULL DEFAULT 0; ALTER TABLE `cloud`.`service_offering` ADD COLUMN `tags` varchar(255); -ALTER TABLE `cloud`.`user_vm` MODIFY COLUMN `domain_router_id` bigint unsigned; -- change from NOT NULL to NULL +ALTER TABLE `cloud`.`user_vm` MODIFY COLUMN `domain_router_id` bigint unsigned; -- change from NOT NULL to NULL -ALTER TABLE `cloud`.`event` ADD COLUMN `state` varchar(32) NOT NULL DEFAULT 'Completed'; +ALTER TABLE `cloud`.`event` ADD COLUMN `state` varchar(32) NOT NULL DEFAULT 'Completed'; ALTER TABLE `cloud`.`event` ADD COLUMN `start_id` bigint unsigned NOT NULL DEFAULT 0; ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `tags` varchar(4096); diff --git a/engine/schema/src/main/resources/META-INF/db/schema-217to218.sql b/engine/schema/src/main/resources/META-INF/db/schema-217to218.sql index f2b6b291f782..006a3f1cd7b3 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-217to218.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-217to218.sql @@ -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/engine/schema/src/main/resources/META-INF/db/schema-21to22-cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-21to22-cleanup.sql index c8757833fabf..8a3ca39d5e7a 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-21to22-cleanup.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-21to22-cleanup.sql @@ -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/engine/schema/src/main/resources/META-INF/db/schema-21to22-premium.sql b/engine/schema/src/main/resources/META-INF/db/schema-21to22-premium.sql index 45202840565f..a34b65dc8ab5 100755 --- a/engine/schema/src/main/resources/META-INF/db/schema-21to22-premium.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-21to22-premium.sql @@ -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 @@ -50,7 +50,7 @@ CREATE TABLE `cloud_usage`.`usage_event` ( `resource_name` varchar(255), `offering_id` bigint unsigned, `template_id` bigint unsigned, - `size` bigint unsigned, + `size` bigint unsigned, `processed` tinyint NOT NULL default '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-21to22.sql b/engine/schema/src/main/resources/META-INF/db/schema-21to22.sql index eb473cfc7f6b..8da29caae53b 100755 --- a/engine/schema/src/main/resources/META-INF/db/schema-21to22.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-21to22.sql @@ -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 @@ -22,7 +22,7 @@ ALTER TABLE `cloud`.`cluster` ADD COLUMN `guid` varchar(255) UNIQUE DEFAULT NULL ALTER TABLE `cloud`.`cluster` ADD COLUMN `cluster_type` varchar(64) DEFAULT 'CloudManaged'; ALTER TABLE `cloud`.`vm_template` ADD COLUMN `hypervisor_type` varchar(32) COMMENT 'hypervisor that the template is belonged to'; ALTER TABLE `cloud`.`vm_template` ADD COLUMN `extractable` int(1) unsigned NOT NULL default 0 COMMENT 'Is this template extractable'; -ALTER TABLE `cloud`.`template_spool_ref` ADD CONSTRAINT `fk_template_spool_ref__template_id` FOREIGN KEY (`template_id`) REFERENCES `vm_template`(`id`); +ALTER TABLE `cloud`.`template_spool_ref` ADD CONSTRAINT `fk_template_spool_ref__template_id` FOREIGN KEY (`template_id`) REFERENCES `vm_template`(`id`); ALTER TABLE `cloud`.`guest_os` modify `name` varchar(255) ; @@ -104,7 +104,7 @@ CREATE TABLE `cloud`.`networks` ( `broadcast_domain_type` varchar(32) NOT NULL COMMENT 'type of broadcast domain used', `broadcast_uri` varchar(255) COMMENT 'broadcast domain specifier', `gateway` varchar(15) COMMENT 'gateway for this network configuration', - `cidr` varchar(18) COMMENT 'network cidr', + `cidr` varchar(18) COMMENT 'network cidr', `mode` varchar(32) COMMENT 'How to retrieve ip address in this network', `network_offering_id` bigint unsigned NOT NULL COMMENT 'network offering id that this configuration is created from', `data_center_id` bigint unsigned NOT NULL COMMENT 'data center id that this configuration is used in', @@ -167,7 +167,7 @@ CREATE TABLE `cloud`.`nics` ( `ip_type` varchar(32) COMMENT 'type of ip', `broadcast_uri` varchar(255) COMMENT 'broadcast uri', `network_id` bigint unsigned NOT NULL COMMENT 'network configuration id', - `mode` varchar(32) COMMENT 'mode of getting ip address', + `mode` varchar(32) COMMENT 'mode of getting ip address', `state` varchar(32) NOT NULL COMMENT 'state of the creation', `strategy` varchar(32) NOT NULL COMMENT 'reservation strategy', `reserver_name` varchar(255) COMMENT 'Name of the component that reserved the ip address', @@ -176,7 +176,7 @@ CREATE TABLE `cloud`.`nics` ( `update_time` timestamp NOT NULL COMMENT 'time the state was changed', `isolation_uri` varchar(255) COMMENT 'id for isolation', `ip6_address` char(40) COMMENT 'ip6 address', - `default_nic` tinyint NOT NULL COMMENT "None", + `default_nic` tinyint NOT NULL COMMENT "None", `created` datetime NOT NULL COMMENT 'date created', `removed` datetime COMMENT 'date removed if not null', PRIMARY KEY (`id`), @@ -253,7 +253,7 @@ CREATE TABLE `cloud`.`op_host` ( `id` bigint unsigned NOT NULL UNIQUE COMMENT 'host id', `sequence` bigint unsigned DEFAULT 1 NOT NULL COMMENT 'sequence for the host communication', PRIMARY KEY (`id`), - CONSTRAINT `fk_op_host__id` FOREIGN KEY (`id`) REFERENCES `host`(`id`) ON DELETE CASCADE + CONSTRAINT `fk_op_host__id` FOREIGN KEY (`id`) REFERENCES `host`(`id`) ON DELETE CASCADE ) ENGINE = InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `cloud`.`guest_os_hypervisor` ( @@ -261,7 +261,7 @@ CREATE TABLE `cloud`.`guest_os_hypervisor` ( `hypervisor_type` varchar(32) NOT NULL, `guest_os_name` varchar(255) NOT NULL, `guest_os_id` bigint unsigned NOT NULL, - PRIMARY KEY (`id`) + PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; INSERT INTO op_host(id, sequence) select id, sequence from host; @@ -269,7 +269,7 @@ INSERT INTO op_host(id, sequence) select id, sequence from host; -- Alter Tables to add Columns; ALTER TABLE `cloud`.`cluster` ADD COLUMN `hypervisor_type` varchar(32); -UPDATE `cloud`.`cluster` SET hypervisor_type=(SELECT DISTINCT host.hypervisor_type from host where host.cluster_id = cluster.id GROUP BY host.hypervisor_type); +UPDATE `cloud`.`cluster` SET hypervisor_type=(SELECT DISTINCT host.hypervisor_type from host where host.cluster_id = cluster.id GROUP BY host.hypervisor_type); ALTER TABLE `cloud`.`volumes` ADD COLUMN `attached` datetime; UPDATE `cloud`.`volumes` SET attached=now() WHERE removed IS NULL AND instance_id IS NOT NULL; @@ -286,7 +286,7 @@ ALTER TABLE `cloud`.`vlan` ADD COLUMN `network_id` bigint unsigned NOT NULL; ALTER TABLE `cloud`.`data_center` ADD COLUMN `domain` varchar(100); ALTER TABLE `cloud`.`data_center` ADD COLUMN `domain_id` bigint unsigned; -ALTER TABLE `cloud`.`data_center` ADD COLUMN `networktype` varchar(255) NOT NULL DEFAULT 'Basic'; +ALTER TABLE `cloud`.`data_center` ADD COLUMN `networktype` varchar(255) NOT NULL DEFAULT 'Basic'; ALTER TABLE `cloud`.`data_center` ADD COLUMN `dns_provider` char(64) DEFAULT 'VirtualRouter'; ALTER TABLE `cloud`.`data_center` ADD COLUMN `gateway_provider` char(64) DEFAULT 'VirtualRouter'; ALTER TABLE `cloud`.`data_center` ADD COLUMN `firewall_provider` char(64) DEFAULT 'VirtualRouter'; @@ -306,7 +306,7 @@ UPDATE `cloud`.`op_dc_link_local_ip_address_alloc` SET reservation_id=concat(cas ALTER TABLE `cloud`.`host_pod_ref` ADD COLUMN `enabled` tinyint NOT NULL DEFAULT 1; ALTER TABLE `cloud`.`op_dc_vnet_alloc` ADD COLUMN `reservation_id` char(40) NULL; -UPDATE op_dc_vnet_alloc set reservation_id=concat(cast(data_center_id as CHAR), concat("-", vnet)) WHERE taken is NOT NULL; +UPDATE op_dc_vnet_alloc set reservation_id=concat(cast(data_center_id as CHAR), concat("-", vnet)) WHERE taken is NOT NULL; ALTER TABLE `cloud`.`vm_instance` ADD COLUMN `service_offering_id` bigint unsigned NOT NULL; ALTER TABLE `cloud`.`vm_instance` ADD COLUMN `reservation_id` char(40); @@ -326,7 +326,7 @@ ALTER TABLE `cloud`.`user_vm` ADD COLUMN `display_name` varchar(255); UPDATE user_vm inner join vm_instance on user_vm.id=vm_instance.id set user_vm.iso_id=vm_instance.iso_id, user_vm.display_name=vm_instance.display_name where vm_instance.type='User'; ALTER TABLE `cloud`.`template_host_ref` ADD COLUMN `physical_size` bigint unsigned DEFAULT 0; -UPDATE template_host_ref INNER JOIN template_spool_ref ON template_host_ref.template_id=template_spool_ref.template_id SET template_host_ref.physical_size=template_spool_ref.template_size; +UPDATE template_host_ref INNER JOIN template_spool_ref ON template_host_ref.template_id=template_spool_ref.template_id SET template_host_ref.physical_size=template_spool_ref.template_size; CREATE TABLE `cloud`.`user_vm_details` ( @@ -412,7 +412,7 @@ CREATE TABLE `cloud`.`vpn_users` ( CONSTRAINT `fk_vpn_users__owner_id` FOREIGN KEY (`owner_id`) REFERENCES `account`(`id`) ON DELETE CASCADE, CONSTRAINT `fk_vpn_users__domain_id` FOREIGN KEY (`domain_id`) REFERENCES `domain`(`id`) ON DELETE CASCADE, INDEX `i_vpn_users_username`(`username`), - UNIQUE `i_vpn_users__account_id__username`(`owner_id`, `username`) + UNIQUE `i_vpn_users__account_id__username`(`owner_id`, `username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ALTER TABLE `cloud`.`storage_pool` ADD COLUMN `status` varchar(32); @@ -490,7 +490,7 @@ CREATE TABLE `cloud`.`usage_event` ( `resource_name` varchar(255), `offering_id` bigint unsigned, `template_id` bigint unsigned, - `size` bigint unsigned, + `size` bigint unsigned, `processed` tinyint NOT NULL default '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-2210to2211.sql b/engine/schema/src/main/resources/META-INF/db/schema-2210to2211.sql index 01bec020d63d..f352f5ef44d6 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-2210to2211.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-2210to2211.sql @@ -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/engine/schema/src/main/resources/META-INF/db/schema-2211to2212-premium.sql b/engine/schema/src/main/resources/META-INF/db/schema-2211to2212-premium.sql index 0cb187e2b9d4..eae13acc78cb 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-2211to2212-premium.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-2211to2212-premium.sql @@ -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/engine/schema/src/main/resources/META-INF/db/schema-2211to2212.sql b/engine/schema/src/main/resources/META-INF/db/schema-2211to2212.sql index 94c3d75a29ff..00d0fcaac76e 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-2211to2212.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-2211to2212.sql @@ -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/engine/schema/src/main/resources/META-INF/db/schema-2212to2213.sql b/engine/schema/src/main/resources/META-INF/db/schema-2212to2213.sql index 2e86599f7924..c69809e8205b 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-2212to2213.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-2212to2213.sql @@ -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/engine/schema/src/main/resources/META-INF/db/schema-2213to2214.sql b/engine/schema/src/main/resources/META-INF/db/schema-2213to2214.sql index 6c0cc4b6c708..41e3944dc180 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-2213to2214.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-2213to2214.sql @@ -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 @@ -29,7 +29,7 @@ CREATE TABLE `cloud`.`mshost_peer` ( `peer_runid` bigint NOT NULL, `peer_state` varchar(10) NOT NULL DEFAULT 'Down', `last_update` DATETIME NULL COMMENT 'Last record update time', - + PRIMARY KEY (`id`), CONSTRAINT `fk_mshost_peer__owner_mshost` FOREIGN KEY (`owner_mshost`) REFERENCES `mshost`(`id`) ON DELETE CASCADE, CONSTRAINT `fk_mshost_peer__peer_mshost` FOREIGN KEY (`peer_mshost`) REFERENCES `mshost`(`id`), diff --git a/engine/schema/src/main/resources/META-INF/db/schema-2214to30-cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-2214to30-cleanup.sql index c90707c75b71..844280d29b64 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-2214to30-cleanup.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-2214to30-cleanup.sql @@ -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 @@ -22,7 +22,7 @@ ALTER TABLE `cloud_usage`.`usage_network` DROP COLUMN `current_bytes_sent`; ALTER TABLE `cloud`.`template_host_ref` DROP COLUMN `pool_id`; DELETE from `cloud`.`op_host_capacity` where capacity_type in (2,4,6); -ALTER TABLE `cloud`.`vm_instance` DROP COLUMN `private_netmask`; +ALTER TABLE `cloud`.`vm_instance` DROP COLUMN `private_netmask`; ALTER TABLE `cloud`.`security_group_rule` drop foreign key `fk_security_ingress_rule___security_group_id`; ALTER TABLE `cloud`.`security_group_rule` drop foreign key `fk_security_ingress_rule___allowed_network_id`; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-2214to30.sql b/engine/schema/src/main/resources/META-INF/db/schema-2214to30.sql index 22fda616649b..8be481e47368 100755 --- a/engine/schema/src/main/resources/META-INF/db/schema-2214to30.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-2214to30.sql @@ -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 @@ -54,7 +54,7 @@ CREATE TABLE `cloud`.`projects` ( PRIMARY KEY (`id`), CONSTRAINT `fk_projects__project_account_id` FOREIGN KEY(`project_account_id`) REFERENCES `account`(`id`) ON DELETE CASCADE, CONSTRAINT `fk_projects__domain_id` FOREIGN KEY(`domain_id`) REFERENCES `domain`(`id`) ON DELETE CASCADE, - INDEX `i_projects__removed`(`removed`) + INDEX `i_projects__removed`(`removed`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -177,97 +177,97 @@ ALTER TABLE `cloud`.`alert` ADD `cluster_id` bigint unsigned; ALTER TABLE `cloud`.`user_statistics` ADD COLUMN `agg_bytes_received` bigint unsigned NOT NULL default '0'; ALTER TABLE `cloud`.`user_statistics` ADD COLUMN `agg_bytes_sent` bigint unsigned NOT NULL default '0'; -ALTER TABLE `cloud`.`vm_instance` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`vm_instance` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`vm_instance` ADD CONSTRAINT `uc_vm_instance_uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`async_job` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`async_job` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`async_job` ADD CONSTRAINT `uc_async__uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`domain` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`domain` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`domain` ADD CONSTRAINT `uc_domain__uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`account` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`account` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`account` ADD CONSTRAINT `uc_account__uuid` UNIQUE (`uuid`); ALTER TABLE `cloud_usage`.`account` ADD COLUMN `uuid` varchar(40); -ALTER TABLE `cloud`.`user` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`user` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`user` ADD CONSTRAINT `uc_user__uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`projects` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`projects` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`projects` ADD CONSTRAINT `uc_projects__uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`data_center` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`data_center` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`data_center` ADD CONSTRAINT `uc_data_center__uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`host` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`host` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`host` ADD CONSTRAINT `uc_host__uuid` UNIQUE (`uuid`); ALTER TABLE `cloud`.`host` ADD COLUMN `update_count` bigint unsigned NOT NULL DEFAULT 0 COMMENT 'atomic increase count making status update operation atomical'; -ALTER TABLE `cloud`.`vm_template` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`vm_template` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`vm_template` ADD CONSTRAINT `uc_vm_template__uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`disk_offering` ADD CONSTRAINT `uc_disk_offering__uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`networks` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`networks` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`networks` ADD CONSTRAINT `uc_networks__uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`security_group` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`security_group` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`security_group` ADD CONSTRAINT `uc_security_group__uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`instance_group` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`instance_group` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`instance_group` ADD CONSTRAINT `uc_instance_group__uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`host_pod_ref` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`host_pod_ref` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`host_pod_ref` ADD CONSTRAINT `uc_host_pod_ref__uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`snapshots` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`snapshots` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`snapshots` ADD CONSTRAINT `uc_snapshots__uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`snapshot_policy` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`snapshot_policy` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`snapshot_policy` ADD CONSTRAINT `uc_snapshot_policy__uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`snapshot_schedule` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`snapshot_schedule` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`snapshot_schedule` ADD CONSTRAINT `uc_snapshot_schedule__uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`volumes` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`volumes` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`volumes` ADD CONSTRAINT `uc_volumes__uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`vlan` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`vlan` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`vlan` ADD CONSTRAINT `uc_vlan__uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`user_ip_address` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`user_ip_address` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`user_ip_address` ADD CONSTRAINT `uc_user_ip_address__uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`firewall_rules` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`firewall_rules` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`firewall_rules` ADD CONSTRAINT `uc_firewall_rules__uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`cluster` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`cluster` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`cluster` ADD CONSTRAINT `uc_cluster__uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`network_offerings` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`network_offerings` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`network_offerings` ADD CONSTRAINT `uc_network_offerings__uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`hypervisor_capabilities` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`hypervisor_capabilities` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`hypervisor_capabilities` ADD CONSTRAINT `uc_hypervisor_capabilities__uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`vpn_users` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`vpn_users` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`vpn_users` ADD CONSTRAINT `uc_vpn_users__uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`event` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`event` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`event` ADD CONSTRAINT `uc_event__uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`alert` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`alert` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`alert` ADD CONSTRAINT `uc_alert__uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`guest_os` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`guest_os` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`guest_os` ADD CONSTRAINT `uc_guest_os__uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`guest_os_category` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`guest_os_category` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`guest_os_category` ADD CONSTRAINT `uc_guest_os_category__uuid` UNIQUE (`uuid`); -ALTER TABLE `cloud`.`nics` ADD COLUMN `uuid` varchar(40); +ALTER TABLE `cloud`.`nics` ADD COLUMN `uuid` varchar(40); ALTER TABLE `cloud`.`nics` ADD CONSTRAINT `uc_nics__uuid` UNIQUE (`uuid`); ALTER TABLE `cloud`.`op_host_capacity` ADD COLUMN `created` datetime; @@ -304,7 +304,7 @@ ALTER TABLE `cloud_usage`.`usage_ip_address` ADD COLUMN `is_system` smallint(1) INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Premium', 'DEFAULT', 'management-server', 'usage.sanity.check.interval', null, 'Interval (in days) to check sanity of usage data'); DELETE FROM `cloud`.`configuration` WHERE name='host.capacity.checker.wait'; -DELETE FROM `cloud`.`configuration` WHERE name='host.capacity.checker.interval'; +DELETE FROM `cloud`.`configuration` WHERE name='host.capacity.checker.interval'; INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'disable.extraction' , 'false', 'Flag for disabling extraction of template, isos and volumes'); INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'NetworkManager', 'router.check.interval' , '30', 'Interval (in seconds) to report redundant router status.'); @@ -362,9 +362,9 @@ CREATE TABLE `cloud`.`physical_network` ( `name` varchar(255) NOT NULL, `data_center_id` bigint unsigned NOT NULL COMMENT 'data center id that this physical network belongs to', `vnet` varchar(255), - `speed` varchar(32), + `speed` varchar(32), `domain_id` bigint unsigned COMMENT 'foreign key to domain id', - `broadcast_domain_range` varchar(32) NOT NULL DEFAULT 'POD' COMMENT 'range of broadcast domain : POD/ZONE', + `broadcast_domain_range` varchar(32) NOT NULL DEFAULT 'POD' COMMENT 'range of broadcast domain : POD/ZONE', `state` varchar(32) NOT NULL DEFAULT 'Disabled' COMMENT 'what state is this configuration in', `created` datetime COMMENT 'date created', `removed` datetime COMMENT 'date removed if not null', @@ -372,7 +372,7 @@ CREATE TABLE `cloud`.`physical_network` ( CONSTRAINT `fk_physical_network__data_center_id` FOREIGN KEY (`data_center_id`) REFERENCES `data_center`(`id`) ON DELETE CASCADE, CONSTRAINT `fk_physical_network__domain_id` FOREIGN KEY(`domain_id`) REFERENCES `domain`(`id`), CONSTRAINT `uc_physical_networks__uuid` UNIQUE (`uuid`), - INDEX `i_physical_network__removed`(`removed`) + INDEX `i_physical_network__removed`(`removed`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `cloud`.`physical_network_tags` ( @@ -690,7 +690,7 @@ CREATE TABLE `cloud_usage`.`usage_security_group` ( `vm_instance_id` bigint unsigned NOT NULL, `security_group_id` bigint unsigned NOT NULL, `created` DATETIME NOT NULL, - `deleted` DATETIME NULL + `deleted` DATETIME NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ALTER TABLE `cloud_usage`.`usage_security_group` ADD INDEX `i_usage_security_group__account_id`(`account_id`); diff --git a/engine/schema/src/main/resources/META-INF/db/schema-221to222-cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-221to222-cleanup.sql index d999b9391031..5ed5f834f682 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-221to222-cleanup.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-221to222-cleanup.sql @@ -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/engine/schema/src/main/resources/META-INF/db/schema-221to222-premium.sql b/engine/schema/src/main/resources/META-INF/db/schema-221to222-premium.sql index 5477fd8a348b..01603a406588 100755 --- a/engine/schema/src/main/resources/META-INF/db/schema-221to222-premium.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-221to222-premium.sql @@ -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/engine/schema/src/main/resources/META-INF/db/schema-221to222.sql b/engine/schema/src/main/resources/META-INF/db/schema-221to222.sql index 0c663b1ca033..d07b71fb6cbe 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-221to222.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-221to222.sql @@ -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 @@ -23,7 +23,7 @@ update network_offerings set firewall_service=1, lb_service=1,vpn_service=1,gate alter table domain add column `state` char(32) NOT NULL default 'Active' COMMENT 'state of the domain'; alter table nics add column `vm_type` char(32); update nics set vm_type=(select type from vm_instance where vm_instance.id=nics.instance_id); -INSERT INTO configuration (`category`, `instance`, `component`, `name`, `value`, `description`) VALUES ('Network','DEFAULT','none','network.guest.cidr.limit','22','size limit for guest cidr; cant be less than this value'); +INSERT INTO configuration (`category`, `instance`, `component`, `name`, `value`, `description`) VALUES ('Network','DEFAULT','none','network.guest.cidr.limit','22','size limit for guest cidr; cant be less than this value'); alter table user_statistics add column `network_id` bigint unsigned; update op_networks set nics_count=(nics_count-1) where id in (select d.network_id from domain_router d, vm_instance i where i.state='Running' and i.id=d.id); update network_offerings set traffic_type='Guest' where system_only=0; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-222to224-cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-222to224-cleanup.sql index 1bcd5d4c5f0a..31f6eefb5fa5 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-222to224-cleanup.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-222to224-cleanup.sql @@ -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/engine/schema/src/main/resources/META-INF/db/schema-222to224-premium.sql b/engine/schema/src/main/resources/META-INF/db/schema-222to224-premium.sql index 9a5f62794c48..33d954d3a884 100755 --- a/engine/schema/src/main/resources/META-INF/db/schema-222to224-premium.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-222to224-premium.sql @@ -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/engine/schema/src/main/resources/META-INF/db/schema-222to224.sql b/engine/schema/src/main/resources/META-INF/db/schema-222to224.sql index 8be64169b448..abbaf447bba6 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-222to224.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-222to224.sql @@ -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 @@ -99,7 +99,7 @@ ALTER TABLE `cloud`.`op_host_capacity` MODIFY `used_capacity` bigint signed NOT ALTER TABLE `cloud`.`op_host_capacity` MODIFY `reserved_capacity` bigint signed NOT NULL; ALTER TABLE `cloud`.`op_host_capacity` MODIFY `total_capacity` bigint signed NOT NULL; -INSERT IGNORE INTO `cloud`.`configuration` VALUES +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced','DEFAULT','management-server','control.cidr','169.254.0.0/16','Changes the cidr for the control network traffic. Defaults to using link local. Must be unique within pods'), ('Advanced','DEFAULT','management-server','control.gateway','169.254.0.1','gateway for the control network traffic'), ('Advanced','DEFAULT','AgentManager','cmd.wait','7200','Time (in seconds) to wait for some heavy time-consuming commands'), @@ -110,7 +110,7 @@ INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Console Proxy','DEFAULT','AgentManager','consoleproxy.url.domain','realhostip.com','Console proxy url domain'), ('Advanced','DEFAULT','management-server','extract.url.cleanup.interval','120','The interval (in seconds) to wait before cleaning up the extract URL\'s '), ('Network','DEFAULT','AgentManager','guest.ip.network','10.1.1.1','The network address of the guest virtual network. Virtual machines will be assigned an IP in this subnet.'), -('Network','DEFAULT','AgentManager','guest.netmask','255.255.255.0','The netmask of the guest virtual network.'), +('Network','DEFAULT','AgentManager','guest.netmask','255.255.255.0','The netmask of the guest virtual network.'), ('Network','DEFAULT','management-server','guest.vlan.bits','12','The number of bits to reserve for the VLAN identifier in the guest subnet.'), ('Advanced','DEFAULT','management-server','host.capacity.checker.interval','3600','Time (in seconds) to wait before recalculating host\'s capacity'), ('Advanced','DEFAULT','management-server','host.capacity.checker.wait','3600','Time (in seconds) to wait before starting host capacity background checker'), @@ -175,7 +175,7 @@ ALTER TABLE `cloud`.`snapshot_schedule` ADD UNIQUE KEY `volume_id` (`volume_id` ALTER TABLE `cloud`.`storage_pool` MODIFY COLUMN `uuid` varchar(255) UNIQUE; ALTER TABLE `cloud`.`user_statistics` DROP KEY `account_id`; -ALTER TABLE `cloud`.`user_statistics` ADD UNIQUE KEY `account_id` (`account_id`,`data_center_id`, `public_ip_address`, `device_id`,`device_type`); +ALTER TABLE `cloud`.`user_statistics` ADD UNIQUE KEY `account_id` (`account_id`,`data_center_id`, `public_ip_address`, `device_id`,`device_type`); ALTER TABLE `cloud`.`usage_event` ADD INDEX `i_usage_event__created`(`created`); diff --git a/engine/schema/src/main/resources/META-INF/db/schema-224to225-cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-224to225-cleanup.sql index b018d7f280e7..5f24290a1fd2 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-224to225-cleanup.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-224to225-cleanup.sql @@ -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/engine/schema/src/main/resources/META-INF/db/schema-224to225.sql b/engine/schema/src/main/resources/META-INF/db/schema-224to225.sql index 65334af306fb..735c30a943c0 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-224to225.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-224to225.sql @@ -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/engine/schema/src/main/resources/META-INF/db/schema-225to226.sql b/engine/schema/src/main/resources/META-INF/db/schema-225to226.sql index ec1baae2e69e..3527921af840 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-225to226.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-225to226.sql @@ -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/engine/schema/src/main/resources/META-INF/db/schema-227to228-premium.sql b/engine/schema/src/main/resources/META-INF/db/schema-227to228-premium.sql index 40fcbfa68380..2e21f0c249ef 100755 --- a/engine/schema/src/main/resources/META-INF/db/schema-227to228-premium.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-227to228-premium.sql @@ -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/engine/schema/src/main/resources/META-INF/db/schema-227to228.sql b/engine/schema/src/main/resources/META-INF/db/schema-227to228.sql index 343c7663fd29..ac3c014894dd 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-227to228.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-227to228.sql @@ -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 @@ -136,9 +136,9 @@ ALTER TABLE `cloud`.`vm_instance` ADD COLUMN `vm_type` varchar(32) NOT NULL; UPDATE vm_instance set vm_type=type; ALTER TABLE `cloud`.`networks` ADD COLUMN `is_domain_specific` int(1) unsigned NOT NULL DEFAULT 0 COMMENT '1 if network is domain specific, 0 false otherwise'; -INSERT INTO configuration (`category`, `instance`, `component`, `name`, `value`, `description`) VALUES ('Advanced', 'DEFAULT', 'NetworkManager', 'allow.subdomain.network.access', 'true', 'Allow subdomains to use networks dedicated to their parent domain(s)'); +INSERT INTO configuration (`category`, `instance`, `component`, `name`, `value`, `description`) VALUES ('Advanced', 'DEFAULT', 'NetworkManager', 'allow.subdomain.network.access', 'true', 'Allow subdomains to use networks dedicated to their parent domain(s)'); -INSERT INTO configuration (`category`, `instance`, `component`, `name`, `value`, `description`) VALUES ('Advanced', 'DEFAULT', 'management-server', 'encode.api.response', 'false', 'Do UTF-8 encoding for the api response, false by default'); +INSERT INTO configuration (`category`, `instance`, `component`, `name`, `value`, `description`) VALUES ('Advanced', 'DEFAULT', 'management-server', 'encode.api.response', 'false', 'Do UTF-8 encoding for the api response, false by default'); DELETE FROM load_balancer_vm_map WHERE instance_id IN (SELECT id FROM vm_instance WHERE removed IS NOT NULL); diff --git a/engine/schema/src/main/resources/META-INF/db/schema-228to229.sql b/engine/schema/src/main/resources/META-INF/db/schema-228to229.sql index 9d5baa4c4032..2496dd4c472f 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-228to229.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-228to229.sql @@ -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/engine/schema/src/main/resources/META-INF/db/schema-229to2210.sql b/engine/schema/src/main/resources/META-INF/db/schema-229to2210.sql index 9c5c46242af8..1d2980f65640 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-229to2210.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-229to2210.sql @@ -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/engine/schema/src/main/resources/META-INF/db/schema-22beta1to22beta2.sql b/engine/schema/src/main/resources/META-INF/db/schema-22beta1to22beta2.sql index 1b7c6a64eb0b..dd03e61e17e5 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-22beta1to22beta2.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-22beta1to22beta2.sql @@ -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/engine/schema/src/main/resources/META-INF/db/schema-22beta3to22beta4.sql b/engine/schema/src/main/resources/META-INF/db/schema-22beta3to22beta4.sql index c73d16537acb..a93d6d5d7e53 100755 --- a/engine/schema/src/main/resources/META-INF/db/schema-22beta3to22beta4.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-22beta3to22beta4.sql @@ -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 @@ -83,7 +83,7 @@ CREATE TABLE `cloud`.`user_vm_details` ( `value` varchar(1024) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; - + CREATE TABLE `cloud`.`cluster_details` ( `id` bigint unsigned NOT NULL auto_increment, `cluster_id` bigint unsigned NOT NULL COMMENT 'cluster id', @@ -99,9 +99,9 @@ ALTER TABLE `cloud`.`service_offering` ADD COLUMN `host_tag` varchar(255); ALTER TABLE `cloud`.`op_it_work` change created created_at bigint unsigned NOT NULL COMMENT 'when was this work detail created'; ALTER TABLE `cloud`.`op_it_work` change state step char(32) NOT NULL COMMENT 'state'; ALTER TABLE `cloud`.`op_it_work` change cancel_taken updated_at bigint unsigned NOT NULL COMMENT 'time it was taken over'; -ALTER TABLE `cloud`.`op_it_work` ADD COLUMN `instance_id` bigint unsigned NOT NULL COMMENT 'vm instance'; -ALTER TABLE `cloud`.`op_it_work` ADD COLUMN `resource_id` bigint unsigned COMMENT 'resource id being worked on'; -ALTER TABLE `cloud`.`op_it_work` ADD COLUMN `resource_type` char(32) COMMENT 'type of resource being worked on'; +ALTER TABLE `cloud`.`op_it_work` ADD COLUMN `instance_id` bigint unsigned NOT NULL COMMENT 'vm instance'; +ALTER TABLE `cloud`.`op_it_work` ADD COLUMN `resource_id` bigint unsigned COMMENT 'resource id being worked on'; +ALTER TABLE `cloud`.`op_it_work` ADD COLUMN `resource_type` char(32) COMMENT 'type of resource being worked on'; ALTER TABLE `cloud`.`hypervsior_properties` ADD COLUMN `is_default` int(1) unsigned NOT NULL DEFAULT 0 COMMENT '1 if network is default'; ALTER TABLE `cloud`.`network_offerings` drop column TYPE; ALTER TABLE `cloud`.`domain_router` ADD COLUMN `host_tag` varchar(255) COMMENT 'host tag specified by the service_offering'; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-301to302-cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-301to302-cleanup.sql index 7922d98ea99a..d32644f471c9 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-301to302-cleanup.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-301to302-cleanup.sql @@ -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/engine/schema/src/main/resources/META-INF/db/schema-301to302.sql b/engine/schema/src/main/resources/META-INF/db/schema-301to302.sql index 4532757d052b..99a555dedcab 100755 --- a/engine/schema/src/main/resources/META-INF/db/schema-301to302.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-301to302.sql @@ -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 @@ -41,7 +41,7 @@ INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Hidden', 'DEFAULT', 'managem INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Network', 'DEFAULT', 'management-server', 'network.disable.rpfilter', 'true', 'disable rp_filter on Domain Router VM public interfaces.'); INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Network', 'DEFAULT', 'management-server', 'network.securitygroups.work.cleanup.interval', '120', 'Time interval (seconds) in which finished work is cleaned up from the work table'); INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Network', 'DEFAULT', 'management-server', 'network.securitygroups.work.lock.timeout', '300', 'Lock wait timeout (seconds) while updating the security group work queues'); -INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Network', 'DEFAULT', 'management-server', 'network.securitygroups.work.per.agent.queue.size', '100', +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Network', 'DEFAULT', 'management-server', 'network.securitygroups.work.per.agent.queue.size', '100', 'The number of outstanding security group work items that can be queued to a host. If exceeded, work items will get dropped to conserve memory. Security Group Sync will take care of ensuring that the host gets updated eventually'); INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Network', 'DEFAULT', 'management-server', 'network.securitygroups.workers.pool.size', '50', 'Number of worker threads processing the security group update work queue'); INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Hidden', 'DEFAULT', 'management-server', 'ovm.guest.network.device', null, 'Specify the private bridge on host for private network'); diff --git a/engine/schema/src/main/resources/META-INF/db/schema-302to303.sql b/engine/schema/src/main/resources/META-INF/db/schema-302to303.sql index b475a8e99580..07faf98cf743 100755 --- a/engine/schema/src/main/resources/META-INF/db/schema-302to303.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-302to303.sql @@ -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 @@ -55,7 +55,7 @@ CREATE TABLE `cloud`.`volume_host_ref` ( `local_path` varchar(255), `install_path` varchar(255), `url` varchar(255), - `format` varchar(32) NOT NULL COMMENT 'format for the volume', + `format` varchar(32) NOT NULL COMMENT 'format for the volume', `destroyed` tinyint(1) COMMENT 'indicates whether the volume_host entry was destroyed by the user or not', PRIMARY KEY (`id`), CONSTRAINT `fk_volume_host_ref__host_id` FOREIGN KEY `fk_volume_host_ref__host_id` (`host_id`) REFERENCES `host` (`id`) ON DELETE CASCADE, diff --git a/engine/schema/src/main/resources/META-INF/db/schema-302to40-cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-302to40-cleanup.sql index 4d89a078b2d5..540c77161893 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-302to40-cleanup.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-302to40-cleanup.sql @@ -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/engine/schema/src/main/resources/META-INF/db/schema-302to40.sql b/engine/schema/src/main/resources/META-INF/db/schema-302to40.sql index ca99f0106d2c..47e4c3fd6d5d 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-302to40.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-302to40.sql @@ -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 @@ -59,7 +59,7 @@ CREATE TABLE `cloud`.`volume_host_ref` ( `local_path` varchar(255), `install_path` varchar(255), `url` varchar(255), - `format` varchar(32) NOT NULL COMMENT 'format for the volume', + `format` varchar(32) NOT NULL COMMENT 'format for the volume', `destroyed` tinyint(1) COMMENT 'indicates whether the volume_host entry was destroyed by the user or not', PRIMARY KEY (`id`), CONSTRAINT `fk_volume_host_ref__host_id` FOREIGN KEY `fk_volume_host_ref__host_id` (`host_id`) REFERENCES `host` (`id`) ON DELETE CASCADE, @@ -236,9 +236,9 @@ from information_schema.key_column_usage A JOIN information_schema.key_column_usage B ON B.table_name = 'physical_network_service_providers' AND B.COLUMN_NAME = 'provider_name' AND A.COLUMN_NAME ='physical_network_id' AND B.CONSTRAINT_NAME=A.CONSTRAINT_NAME where A.table_name = 'physical_network_service_providers' LIMIT 1); -PREPARE stmt1 FROM @constraintname; -EXECUTE stmt1; -DEALLOCATE PREPARE stmt1; +PREPARE stmt1 FROM @constraintname; +EXECUTE stmt1; +DEALLOCATE PREPARE stmt1; AlTER TABLE `cloud`.`physical_network_service_providers` ADD CONSTRAINT `fk_pnetwork_service_providers__physical_network_id` FOREIGN KEY (`physical_network_id`) REFERENCES `physical_network`(`id`) ON DELETE CASCADE; UPDATE `cloud`.`configuration` SET description='In second, timeout for creating volume from snapshot' WHERE name='create.volume.from.snapshot.wait'; @@ -299,7 +299,7 @@ CREATE TABLE `cloud`.`vpc` ( PRIMARY KEY (`id`), INDEX `i_vpc__removed`(`removed`), CONSTRAINT `fk_vpc__zone_id` FOREIGN KEY `fk_vpc__zone_id` (`zone_id`) REFERENCES `data_center` (`id`) ON DELETE CASCADE, - CONSTRAINT `fk_vpc__vpc_offering_id` FOREIGN KEY (`vpc_offering_id`) REFERENCES `vpc_offerings`(`id`), + CONSTRAINT `fk_vpc__vpc_offering_id` FOREIGN KEY (`vpc_offering_id`) REFERENCES `vpc_offerings`(`id`), CONSTRAINT `fk_vpc__account_id` FOREIGN KEY `fk_vpc__account_id` (`account_id`) REFERENCES `account`(`id`) ON DELETE CASCADE, CONSTRAINT `fk_vpc__domain_id` FOREIGN KEY `fk_vpc__domain_id` (`domain_id`) REFERENCES `domain`(`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -360,7 +360,7 @@ CREATE TABLE `cloud`.`static_routes` ( `id` bigint unsigned NOT NULL auto_increment COMMENT 'id', `uuid` varchar(40), `vpc_gateway_id` bigint unsigned COMMENT 'id of the corresponding ip address', - `cidr` varchar(18) COMMENT 'cidr for the static route', + `cidr` varchar(18) COMMENT 'cidr for the static route', `state` char(32) NOT NULL COMMENT 'current state of this rule', `vpc_id` bigint unsigned COMMENT 'vpc the firewall rule is associated with', `account_id` bigint unsigned NOT NULL COMMENT 'owner id', @@ -468,7 +468,7 @@ UPDATE `cloud`.`configuration` SET description='Comma separated list of cidrs in INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Network', 'DEFAULT', 'management-server', 'site2site.vpn.vpngateway.connection.limit', '4', 'The maximum number of VPN connection per VPN gateway'); INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Network', 'DEFAULT', 'management-server', 'site2site.vpn.customergateway.subnets.limit', '10', 'The maximum number of subnets per customer gateway'); -INSERT IGNORE INTO `cloud`.`guest_os_category` VALUES ('11','None',NULL); +INSERT IGNORE INTO `cloud`.`guest_os_category` VALUES ('11','None',NULL); ALTER TABLE `cloud`.`user` ADD COLUMN `incorrect_login_attempts` integer unsigned NOT NULL DEFAULT '0'; INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'incorrect.login.attempts.allowed', '5', 'Incorrect login attempts allowed before the user is disabled'); UPDATE `cloud`.`configuration` set description ='Uuid of the service offering used by console proxy; if NULL - system offering will be used' where name ='consoleproxy.service.offering'; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-304to305-cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-304to305-cleanup.sql index 3b5c8f5a3565..1184c98e1824 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-304to305-cleanup.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-304to305-cleanup.sql @@ -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/engine/schema/src/main/resources/META-INF/db/schema-304to305.sql b/engine/schema/src/main/resources/META-INF/db/schema-304to305.sql index dfeff3f683bd..cb2efb3edd02 100755 --- a/engine/schema/src/main/resources/META-INF/db/schema-304to305.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-304to305.sql @@ -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 @@ -80,7 +80,7 @@ CREATE TABLE `cloud`.`vpc` ( PRIMARY KEY (`id`), INDEX `i_vpc__removed`(`removed`), CONSTRAINT `fk_vpc__zone_id` FOREIGN KEY `fk_vpc__zone_id` (`zone_id`) REFERENCES `data_center` (`id`) ON DELETE CASCADE, - CONSTRAINT `fk_vpc__vpc_offering_id` FOREIGN KEY (`vpc_offering_id`) REFERENCES `vpc_offerings`(`id`), + CONSTRAINT `fk_vpc__vpc_offering_id` FOREIGN KEY (`vpc_offering_id`) REFERENCES `vpc_offerings`(`id`), CONSTRAINT `fk_vpc__account_id` FOREIGN KEY `fk_vpc__account_id` (`account_id`) REFERENCES `account`(`id`) ON DELETE CASCADE, CONSTRAINT `fk_vpc__domain_id` FOREIGN KEY `fk_vpc__domain_id` (`domain_id`) REFERENCES `domain`(`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -141,7 +141,7 @@ CREATE TABLE `cloud`.`static_routes` ( `id` bigint unsigned NOT NULL auto_increment COMMENT 'id', `uuid` varchar(40), `vpc_gateway_id` bigint unsigned COMMENT 'id of the corresponding ip address', - `cidr` varchar(18) COMMENT 'cidr for the static route', + `cidr` varchar(18) COMMENT 'cidr for the static route', `state` char(32) NOT NULL COMMENT 'current state of this rule', `vpc_id` bigint unsigned COMMENT 'vpc the firewall rule is associated with', `account_id` bigint unsigned NOT NULL COMMENT 'owner id', diff --git a/engine/schema/src/main/resources/META-INF/db/schema-305to306-cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-305to306-cleanup.sql index f15ad4fbc5cb..850d48b6526d 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-305to306-cleanup.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-305to306-cleanup.sql @@ -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/engine/schema/src/main/resources/META-INF/db/schema-305to306.sql b/engine/schema/src/main/resources/META-INF/db/schema-305to306.sql index b1294a21054f..e9a620bb4ccd 100755 --- a/engine/schema/src/main/resources/META-INF/db/schema-305to306.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-305to306.sql @@ -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/engine/schema/src/main/resources/META-INF/db/schema-306to307.sql b/engine/schema/src/main/resources/META-INF/db/schema-306to307.sql index a43833efa437..0ddee9ec5db4 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-306to307.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-306to307.sql @@ -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/engine/schema/src/main/resources/META-INF/db/schema-307to410.sql b/engine/schema/src/main/resources/META-INF/db/schema-307to410.sql index 944d910fec40..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 @@ -22,7 +22,7 @@ SET foreign_key_checks = 0; --- DB upgrade steps from 302-40 +-- DB upgrade steps from 302-40 CREATE TABLE `cloud`.`external_nicira_nvp_devices` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', `uuid` varchar(255) UNIQUE, @@ -59,9 +59,9 @@ from information_schema.key_column_usage A JOIN information_schema.key_column_usage B ON B.table_name = 'physical_network_service_providers' AND B.COLUMN_NAME = 'provider_name' AND A.COLUMN_NAME ='physical_network_id' AND B.CONSTRAINT_NAME=A.CONSTRAINT_NAME where A.table_name = 'physical_network_service_providers' LIMIT 1); -PREPARE stmt1 FROM @constraintname; -EXECUTE stmt1; -DEALLOCATE PREPARE stmt1; +PREPARE stmt1 FROM @constraintname; +EXECUTE stmt1; +DEALLOCATE PREPARE stmt1; AlTER TABLE physical_network_service_providers ADD CONSTRAINT `fk_pnetwork_service_providers__physical_network_id` FOREIGN KEY (`physical_network_id`) REFERENCES `physical_network`(`id`) ON DELETE CASCADE; UPDATE `cloud`.`configuration` SET description='Do URL encoding for the api response, false by default' WHERE name='encode.api.response'; @@ -351,8 +351,8 @@ ALTER TABLE `cloud`.`vlan` ADD COLUMN `ip6_range` varchar(255); ALTER TABLE `cloud`.`data_center` ADD COLUMN `ip6_dns1` varchar(255); ALTER TABLE `cloud`.`data_center` ADD COLUMN `ip6_dns2` varchar(255); -UPDATE `cloud`.`networks` INNER JOIN `cloud`.`vlan` ON networks.id = vlan.network_id -SET networks.gateway = vlan.vlan_gateway, networks.ip6_gateway = vlan.ip6_gateway, networks.ip6_cidr = vlan.ip6_cidr +UPDATE `cloud`.`networks` INNER JOIN `cloud`.`vlan` ON networks.id = vlan.network_id +SET networks.gateway = vlan.vlan_gateway, networks.ip6_gateway = vlan.ip6_gateway, networks.ip6_cidr = vlan.ip6_cidr WHERE networks.data_center_id = vlan.data_center_id AND networks.physical_network_id = vlan.physical_network_id; -- DB views for list api diff --git a/engine/schema/src/main/resources/META-INF/db/schema-30to301.sql b/engine/schema/src/main/resources/META-INF/db/schema-30to301.sql index 0cc51e7d08f7..81339bf2a53d 100755 --- a/engine/schema/src/main/resources/META-INF/db/schema-30to301.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-30to301.sql @@ -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/engine/schema/src/main/resources/META-INF/db/schema-40to410.sql b/engine/schema/src/main/resources/META-INF/db/schema-40to410.sql index 1b3a29b27a45..845b31ca04ac 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-40to410.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-40to410.sql @@ -457,15 +457,15 @@ ALTER TABLE `cloud`.`vlan` ADD COLUMN `ip6_range` varchar(255); ALTER TABLE `cloud`.`data_center` ADD COLUMN `ip6_dns1` varchar(255); ALTER TABLE `cloud`.`data_center` ADD COLUMN `ip6_dns2` varchar(255); -UPDATE `cloud`.`networks` INNER JOIN `cloud`.`vlan` ON networks.id = vlan.network_id -SET networks.gateway = vlan.vlan_gateway, networks.ip6_gateway = vlan.ip6_gateway, networks.ip6_cidr = vlan.ip6_cidr +UPDATE `cloud`.`networks` INNER JOIN `cloud`.`vlan` ON networks.id = vlan.network_id +SET networks.gateway = vlan.vlan_gateway, networks.ip6_gateway = vlan.ip6_gateway, networks.ip6_cidr = vlan.ip6_cidr WHERE networks.data_center_id = vlan.data_center_id AND networks.physical_network_id = vlan.physical_network_id; -- DB views for list api DROP VIEW IF EXISTS `cloud`.`user_vm_view`; CREATE VIEW `cloud`.`user_vm_view` AS - select + select vm_instance.id id, vm_instance.name name, user_vm.display_name display_name, @@ -504,7 +504,7 @@ CREATE VIEW `cloud`.`user_vm_view` AS vm_instance.vm_type vm_type, data_center.id data_center_id, data_center.uuid data_center_uuid, - data_center.name data_center_name, + data_center.name data_center_name, data_center.is_security_group_enabled security_group_enabled, host.id host_id, host.uuid host_uuid, @@ -634,7 +634,7 @@ CREATE VIEW `cloud`.`user_vm_view` AS DROP VIEW IF EXISTS `cloud`.`domain_router_view`; CREATE VIEW `cloud`.`domain_router_view` AS - select + select vm_instance.id id, vm_instance.name name, account.id account_id, @@ -740,7 +740,7 @@ CREATE VIEW `cloud`.`domain_router_view` AS DROP VIEW IF EXISTS `cloud`.`security_group_view`; CREATE VIEW `cloud`.`security_group_view` AS - select + select security_group.id id, security_group.name name, security_group.description description, @@ -799,7 +799,7 @@ CREATE VIEW `cloud`.`security_group_view` AS DROP VIEW IF EXISTS `cloud`.`resource_tag_view`; CREATE VIEW `cloud`.`resource_tag_view` AS - select + select resource_tags.id, resource_tags.uuid, resource_tags.key, @@ -831,7 +831,7 @@ CREATE VIEW `cloud`.`resource_tag_view` AS DROP VIEW IF EXISTS `cloud`.`event_view`; CREATE VIEW `cloud`.`event_view` AS - select + select event.id, event.uuid, event.type, @@ -870,7 +870,7 @@ CREATE VIEW `cloud`.`event_view` AS DROP VIEW IF EXISTS `cloud`.`instance_group_view`; CREATE VIEW `cloud`.`instance_group_view` AS - select + select instance_group.id, instance_group.uuid, instance_group.name, @@ -898,7 +898,7 @@ CREATE VIEW `cloud`.`instance_group_view` AS DROP VIEW IF EXISTS `cloud`.`user_view`; CREATE VIEW `cloud`.`user_view` AS - select + select user.id, user.uuid, user.username, @@ -941,7 +941,7 @@ CREATE VIEW `cloud`.`user_view` AS DROP VIEW IF EXISTS `cloud`.`project_view`; CREATE VIEW `cloud`.`project_view` AS - select + select projects.id, projects.uuid, projects.name, @@ -982,7 +982,7 @@ CREATE VIEW `cloud`.`project_view` AS DROP VIEW IF EXISTS `cloud`.`project_account_view`; CREATE VIEW `cloud`.`project_account_view` AS - select + select project_account.id, account.id account_id, account.uuid account_uuid, @@ -1007,7 +1007,7 @@ CREATE VIEW `cloud`.`project_account_view` AS DROP VIEW IF EXISTS `cloud`.`project_invitation_view`; CREATE VIEW `cloud`.`project_invitation_view` AS - select + select project_invitations.id, project_invitations.uuid, project_invitations.email, @@ -1035,7 +1035,7 @@ CREATE VIEW `cloud`.`project_invitation_view` AS DROP VIEW IF EXISTS `cloud`.`host_view`; CREATE VIEW `cloud`.`host_view` AS - select + select host.id, host.uuid, host.name, @@ -1105,7 +1105,7 @@ CREATE VIEW `cloud`.`host_view` AS DROP VIEW IF EXISTS `cloud`.`volume_view`; CREATE VIEW `cloud`.`volume_view` AS - select + select volumes.id, volumes.uuid, volumes.name, @@ -1206,7 +1206,7 @@ CREATE VIEW `cloud`.`volume_view` AS DROP VIEW IF EXISTS `cloud`.`account_netstats_view`; CREATE VIEW `cloud`.`account_netstats_view` AS - SELECT + SELECT account_id, sum(net_bytes_received) + sum(current_bytes_received) as bytesReceived, sum(net_bytes_sent) + sum(current_bytes_sent) as bytesSent @@ -1217,7 +1217,7 @@ CREATE VIEW `cloud`.`account_netstats_view` AS DROP VIEW IF EXISTS `cloud`.`account_vmstats_view`; CREATE VIEW `cloud`.`account_vmstats_view` AS - SELECT + SELECT account_id, state, count(*) as vmcount from `cloud`.`vm_instance` @@ -1225,7 +1225,7 @@ CREATE VIEW `cloud`.`account_vmstats_view` AS DROP VIEW IF EXISTS `cloud`.`free_ip_view`; CREATE VIEW `cloud`.`free_ip_view` AS - select + select count(user_ip_address.id) free_ip from `cloud`.`user_ip_address` @@ -1237,7 +1237,7 @@ CREATE VIEW `cloud`.`free_ip_view` AS DROP VIEW IF EXISTS `cloud`.`account_view`; CREATE VIEW `cloud`.`account_view` AS - select + select account.id, account.uuid, account.account_name, @@ -1348,7 +1348,7 @@ CREATE VIEW `cloud`.`account_view` AS DROP VIEW IF EXISTS `cloud`.`async_job_view`; CREATE VIEW `cloud`.`async_job_view` AS - select + select account.id account_id, account.uuid account_uuid, account.account_name account_name, @@ -1457,7 +1457,7 @@ CREATE VIEW `cloud`.`async_job_view` AS DROP VIEW IF EXISTS `cloud`.`storage_pool_view`; CREATE VIEW `cloud`.`storage_pool_view` AS - select + select storage_pool.id, storage_pool.uuid, storage_pool.name, @@ -1475,7 +1475,7 @@ CREATE VIEW `cloud`.`storage_pool_view` AS cluster.cluster_type, data_center.id data_center_id, data_center.uuid data_center_uuid, - data_center.name data_center_name, + data_center.name data_center_name, host_pod_ref.id pod_id, host_pod_ref.uuid pod_uuid, host_pod_ref.name pod_name, @@ -1507,7 +1507,7 @@ CREATE VIEW `cloud`.`storage_pool_view` AS DROP VIEW IF EXISTS `cloud`.`disk_offering_view`; CREATE VIEW `cloud`.`disk_offering_view` AS - select + select disk_offering.id, disk_offering.uuid, disk_offering.name, @@ -1532,7 +1532,7 @@ CREATE VIEW `cloud`.`disk_offering_view` AS DROP VIEW IF EXISTS `cloud`.`service_offering_view`; CREATE VIEW `cloud`.`service_offering_view` AS - select + select service_offering.id, disk_offering.uuid, disk_offering.name, @@ -1563,10 +1563,10 @@ CREATE VIEW `cloud`.`service_offering_view` AS `cloud`.`disk_offering` ON service_offering.id = disk_offering.id left join `cloud`.`domain` ON disk_offering.domain_id = domain.id; - + DROP VIEW IF EXISTS `cloud`.`data_center_view`; CREATE VIEW `cloud`.`data_center_view` AS - select + select data_center.id, data_center.uuid, data_center.name, @@ -1593,8 +1593,8 @@ CREATE VIEW `cloud`.`data_center_view` AS from `cloud`.`data_center` left join - `cloud`.`domain` ON data_center.domain_id = domain.id; - + `cloud`.`domain` ON data_center.domain_id = domain.id; + CREATE TABLE `cloud`.`baremetal_dhcp_devices` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41000to41100.sql b/engine/schema/src/main/resources/META-INF/db/schema-41000to41100.sql index 6148ee11bcfd..0b1779d7e2bd 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41000to41100.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41000to41100.sql @@ -19,51 +19,6 @@ -- Schema upgrade from 4.10.0.0 to 4.11.0.0 --; ---; --- Stored procedure to do idempotent column add; ---; -DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_ADD_COLUMN`; - -CREATE PROCEDURE `cloud`.`IDEMPOTENT_ADD_COLUMN` ( - IN in_table_name VARCHAR(200) - , IN in_column_name VARCHAR(200) - , IN in_column_definition VARCHAR(1000) -) -BEGIN - - DECLARE CONTINUE HANDLER FOR 1060 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', 'ADD COLUMN') ; SET @ddl = CONCAT(@ddl, ' ', in_column_name); SET @ddl = CONCAT(@ddl, ' ', in_column_definition); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; - -DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_DROP_FOREIGN_KEY`; - -CREATE PROCEDURE `cloud`.`IDEMPOTENT_DROP_FOREIGN_KEY` ( - IN in_table_name VARCHAR(200) - , IN in_foreign_key_name VARCHAR(200) -) -BEGIN - - DECLARE CONTINUE HANDLER FOR 1091 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', ' DROP FOREIGN KEY '); SET @ddl = CONCAT(@ddl, ' ', in_foreign_key_name); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; - -DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_DROP_INDEX`; - -CREATE PROCEDURE `cloud`.`IDEMPOTENT_DROP_INDEX` ( - IN in_index_name VARCHAR(200) - , IN in_table_name VARCHAR(200) -) -BEGIN - - DECLARE CONTINUE HANDLER FOR 1091 BEGIN END; SET @ddl = CONCAT('DROP INDEX ', in_index_name); SET @ddl = CONCAT(@ddl, ' ', ' ON ') ; SET @ddl = CONCAT(@ddl, ' ', in_table_name); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; - -DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_CREATE_UNIQUE_INDEX`; - -CREATE PROCEDURE `cloud`.`IDEMPOTENT_CREATE_UNIQUE_INDEX` ( - IN in_index_name VARCHAR(200) - , IN in_table_name VARCHAR(200) - , IN in_index_definition VARCHAR(1000) -) -BEGIN - - DECLARE CONTINUE HANDLER FOR 1061 BEGIN END; SET @ddl = CONCAT('CREATE UNIQUE INDEX ', in_index_name); SET @ddl = CONCAT(@ddl, ' ', ' ON ') ; SET @ddl = CONCAT(@ddl, ' ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', in_index_definition); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; - -- Add For VPC flag CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.network_offerings','for_vpc', 'INT(1) NOT NULL DEFAULT 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 3556e7e1b4a2..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 @@ -113,7 +113,7 @@ CREATE TABLE `cloud`.`image_store` ( `uuid` varchar(255) COMMENT 'uuid of data store', `parent` varchar(255) COMMENT 'parent path for the storage server', `created` datetime COMMENT 'date the image store first signed on', - `removed` datetime COMMENT 'date removed if not null', + `removed` datetime COMMENT 'date removed if not null', `total_size` bigint unsigned COMMENT 'storage total size statistics', `used_bytes` bigint unsigned COMMENT 'storage available bytes statistics', PRIMARY KEY(`id`) @@ -131,7 +131,7 @@ CREATE TABLE `cloud`.`image_store_details` ( DROP VIEW IF EXISTS `cloud`.`image_store_view`; CREATE VIEW `cloud`.`image_store_view` AS - select + select image_store.id, image_store.uuid, image_store.name, @@ -153,9 +153,9 @@ CREATE VIEW `cloud`.`image_store_view` AS left join `cloud`.`image_store_details` ON image_store_details.store_id = image_store.id; - + -- here we have to allow null for store_id to accommodate baremetal case to search for ready templates since template state is only stored in this table --- FK also commented out due to this +-- FK also commented out due to this CREATE TABLE `cloud`.`template_store_ref` ( `id` bigint unsigned NOT NULL auto_increment, `store_id` bigint unsigned, @@ -165,7 +165,7 @@ CREATE TABLE `cloud`.`template_store_ref` ( `job_id` varchar(255), `download_pct` int(10) unsigned, `size` bigint unsigned, - `store_role` varchar(255), + `store_role` varchar(255), `physical_size` bigint unsigned DEFAULT 0, `download_state` varchar(255), `error_str` varchar(255), @@ -177,7 +177,7 @@ CREATE TABLE `cloud`.`template_store_ref` ( `is_copy` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'indicates whether this was copied ', `update_count` bigint unsigned, `ref_cnt` bigint unsigned DEFAULT 0, - `updated` datetime, + `updated` datetime, PRIMARY KEY (`id`), -- CONSTRAINT `fk_template_store_ref__store_id` FOREIGN KEY `fk_template_store_ref__store_id` (`store_id`) REFERENCES `image_store` (`id`) ON DELETE CASCADE, INDEX `i_template_store_ref__store_id`(`store_id`), @@ -193,7 +193,7 @@ CREATE TABLE `cloud`.`template_store_ref` ( -- ALTER TABLE `cloud`.`snapshots` DROP COLUMN `sechost_id`; -- change upload host_id FK to point to image_store table -ALTER TABLE `cloud`.`upload` DROP FOREIGN KEY `fk_upload__host_id`; +ALTER TABLE `cloud`.`upload` DROP FOREIGN KEY `fk_upload__host_id`; ALTER TABLE `cloud`.`upload` ADD CONSTRAINT `fk_upload__store_id` FOREIGN KEY(`host_id`) REFERENCES `image_store` (`id`) ON DELETE CASCADE; CREATE TABLE `cloud`.`snapshot_store_ref` ( @@ -208,11 +208,11 @@ CREATE TABLE `cloud`.`snapshot_store_ref` ( `physical_size` bigint unsigned DEFAULT 0, `parent_snapshot_id` bigint unsigned DEFAULT 0, `install_path` varchar(255), - `state` varchar(255) NOT NULL, - -- `removed` datetime COMMENT 'date removed if not null', + `state` varchar(255) NOT NULL, + -- `removed` datetime COMMENT 'date removed if not null', `update_count` bigint unsigned, `ref_cnt` bigint unsigned, - `updated` datetime, + `updated` datetime, `volume_id` bigint unsigned, PRIMARY KEY (`id`), INDEX `i_snapshot_store_ref__store_id`(`store_id`), @@ -238,11 +238,11 @@ CREATE TABLE `cloud`.`volume_store_ref` ( `install_path` varchar(255), `url` varchar(255), `download_url` varchar(255), - `state` varchar(255) NOT NULL, + `state` varchar(255) NOT NULL, `destroyed` tinyint(1) COMMENT 'indicates whether the volume_host entry was destroyed by the user or not', `update_count` bigint unsigned, `ref_cnt` bigint unsigned, - `updated` datetime, + `updated` datetime, PRIMARY KEY (`id`), CONSTRAINT `fk_volume_store_ref__store_id` FOREIGN KEY `fk_volume_store_ref__store_id` (`store_id`) REFERENCES `image_store` (`id`) ON DELETE CASCADE, INDEX `i_volume_store_ref__store_id`(`store_id`), @@ -662,12 +662,12 @@ ALTER TABLE `cloud`.`remote_access_vpn` ADD COLUMN `id` bigint unsigned NOT NULL ALTER TABLE `cloud`.`remote_access_vpn` ADD COLUMN `uuid` varchar(40) UNIQUE; -- START: support for LXC - + INSERT IGNORE INTO `cloud`.`hypervisor_capabilities`(uuid, hypervisor_type, hypervisor_version, max_guests_limit, security_group_enabled) VALUES (UUID(), 'LXC', 'default', 50, 1); ALTER TABLE `cloud`.`physical_network_traffic_types` ADD COLUMN `lxc_network_label` varchar(255) DEFAULT 'cloudbr0' COMMENT 'The network name label of the physical device dedicated to this traffic on a LXC host'; - + UPDATE configuration SET value='KVM,XenServer,VMware,BareMetal,Ovm,LXC' WHERE name='hypervisor.list'; - + INSERT INTO `cloud`.`vm_template` (id, uuid, unique_name, name, public, created, type, hvm, bits, account_id, url, checksum, enable_password, display_text, format, guest_os_id, featured, cross_zones, hypervisor_type) VALUES (10, UUID(), 'routing-10', 'SystemVM Template (LXC)', 0, now(), 'SYSTEM', 0, 64, 1, 'http://download.cloudstack.org/templates/acton/acton-systemvm-02062012.qcow2.bz2', '2755de1f9ef2ce4d6f2bee2efbb4da92', 0, 'SystemVM Template (LXC)', 'QCOW2', 15, 0, 1, 'LXC'); @@ -717,10 +717,10 @@ CREATE TABLE `cloud`.`service_offering_details` ( CONSTRAINT `fk_service_offering_details__service_offering_id` FOREIGN KEY (`service_offering_id`) REFERENCES `service_offering`(`id`) ON DELETE CASCADE, CONSTRAINT UNIQUE KEY `uk_service_offering_id_name` (`service_offering_id`, `name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; - + DROP VIEW IF EXISTS `cloud`.`user_vm_view`; CREATE VIEW `cloud`.`user_vm_view` AS - select + select vm_instance.id id, vm_instance.name name, user_vm.display_name display_name, @@ -898,7 +898,7 @@ CREATE VIEW `cloud`.`user_vm_view` AS DROP VIEW IF EXISTS `cloud`.`affinity_group_view`; CREATE VIEW `cloud`.`affinity_group_view` AS - select + select affinity_group.id id, affinity_group.name name, affinity_group.type type, @@ -933,7 +933,7 @@ CREATE VIEW `cloud`.`affinity_group_view` AS DROP VIEW IF EXISTS `cloud`.`host_view`; CREATE VIEW `cloud`.`host_view` AS - select + select host.id, host.uuid, host.name, @@ -1001,10 +1001,10 @@ CREATE VIEW `cloud`.`host_view` AS `cloud`.`async_job` ON async_job.instance_id = host.id and async_job.instance_type = 'Host' and async_job.job_status = 0; - + DROP VIEW IF EXISTS `cloud`.`storage_pool_view`; CREATE VIEW `cloud`.`storage_pool_view` AS - select + select storage_pool.id, storage_pool.uuid, storage_pool.name, @@ -1024,7 +1024,7 @@ CREATE VIEW `cloud`.`storage_pool_view` AS cluster.cluster_type, data_center.id data_center_id, data_center.uuid data_center_uuid, - data_center.name data_center_name, + data_center.name data_center_name, data_center.networktype data_center_type, host_pod_ref.id pod_id, host_pod_ref.uuid pod_uuid, @@ -1054,11 +1054,11 @@ CREATE VIEW `cloud`.`storage_pool_view` AS `cloud`.`async_job` ON async_job.instance_id = storage_pool.id and async_job.instance_type = 'StoragePool' and async_job.job_status = 0; - + DROP VIEW IF EXISTS `cloud`.`domain_router_view`; CREATE VIEW `cloud`.`domain_router_view` AS - select + select vm_instance.id id, vm_instance.name name, account.id account_id, @@ -1157,7 +1157,7 @@ CREATE VIEW `cloud`.`domain_router_view` AS `cloud`.`async_job` ON async_job.instance_id = vm_instance.id and async_job.instance_type = 'DomainRouter' and async_job.job_status = 0; - + CREATE TABLE `cloud`.`external_cisco_vnmc_devices` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', `uuid` varchar(255) UNIQUE, @@ -1242,7 +1242,7 @@ INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'manag DROP VIEW IF EXISTS `cloud`.`service_offering_view`; CREATE VIEW `cloud`.`service_offering_view` AS - select + select service_offering.id, disk_offering.uuid, disk_offering.name, @@ -1289,7 +1289,7 @@ UPDATE `cloud_usage`.`account` SET `default`=1 WHERE id IN (1,2); UPDATE `cloud`.`user` SET `cloud`.`user`.`default`=1 WHERE id IN (1,2); CREATE OR REPLACE VIEW `cloud`.`user_view` AS - select + select user.id, user.uuid, user.username, @@ -1329,7 +1329,7 @@ CREATE OR REPLACE VIEW `cloud`.`user_view` AS `cloud`.`async_job` ON async_job.instance_id = user.id and async_job.instance_type = 'User' and async_job.job_status = 0; - + DROP VIEW IF EXISTS `cloud`.`account_view`; CREATE VIEW `cloud`.`account_view` AS @@ -1879,7 +1879,7 @@ ALTER TABLE `cloud`.`account_details` MODIFY value varchar(255); DROP VIEW IF EXISTS `cloud`.`template_view`; CREATE VIEW `cloud`.`template_view` AS - select + select vm_template.id, vm_template.uuid, vm_template.unique_name, @@ -1920,7 +1920,7 @@ CREATE VIEW `cloud`.`template_view` AS domain.path domain_path, projects.id project_id, projects.uuid project_uuid, - projects.name project_name, + projects.name project_name, data_center.id data_center_id, data_center.uuid data_center_uuid, data_center.name data_center_name, @@ -1950,23 +1950,23 @@ CREATE VIEW `cloud`.`template_view` AS from `cloud`.`vm_template` inner join - `cloud`.`guest_os` ON guest_os.id = vm_template.guest_os_id + `cloud`.`guest_os` ON guest_os.id = vm_template.guest_os_id inner join `cloud`.`account` ON account.id = vm_template.account_id inner join `cloud`.`domain` ON domain.id = account.domain_id left join - `cloud`.`projects` ON projects.project_account_id = account.id + `cloud`.`projects` ON projects.project_account_id = account.id left join - `cloud`.`vm_template_details` ON vm_template_details.template_id = vm_template.id + `cloud`.`vm_template_details` ON vm_template_details.template_id = vm_template.id left join - `cloud`.`vm_template` source_template ON source_template.id = vm_template.source_template_id + `cloud`.`vm_template` source_template ON source_template.id = vm_template.source_template_id left join `cloud`.`template_store_ref` ON template_store_ref.template_id = vm_template.id and template_store_ref.store_role = 'Image' left join - `cloud`.`image_store` ON image_store.removed is NULL AND template_store_ref.store_id is not NULL AND image_store.id = template_store_ref.store_id + `cloud`.`image_store` ON image_store.removed is NULL AND template_store_ref.store_id is not NULL AND image_store.id = template_store_ref.store_id left join - `cloud`.`template_zone_ref` ON template_zone_ref.template_id = vm_template.id AND template_store_ref.store_id is NULL AND template_zone_ref.removed is null + `cloud`.`template_zone_ref` ON template_zone_ref.template_id = vm_template.id AND template_store_ref.store_id is NULL AND template_zone_ref.removed is null left join `cloud`.`data_center` ON (image_store.data_center_id = data_center.id OR template_zone_ref.zone_id = data_center.id) left join @@ -1974,7 +1974,7 @@ CREATE VIEW `cloud`.`template_view` AS left join `cloud`.`resource_tags` ON resource_tags.resource_id = vm_template.id and (resource_tags.resource_type = 'Template' or resource_tags.resource_type='ISO'); - + INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Network', 'DEFAULT', 'management-server', 'midonet.apiserver.address', 'http://localhost:8081', 'Specify the address at which the Midonet API server can be contacted (if using Midonet)'); INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Network', 'DEFAULT', 'management-server', 'midonet.providerrouter.id', 'd7c5e6a3-e2f4-426b-b728-b7ce6a0448e5', 'Specifies the UUID of the Midonet provider router (if using Midonet)'); @@ -1996,7 +1996,7 @@ CREATE TABLE `cloud`.`account_vnet_map` ( ALTER TABLE `cloud`.`op_dc_vnet_alloc` ADD COLUMN account_vnet_map_id bigint unsigned; ALTER TABLE `cloud`.`op_dc_vnet_alloc` ADD CONSTRAINT `fk_op_dc_vnet_alloc__account_vnet_map_id` FOREIGN KEY `fk_op_dc_vnet_alloc__account_vnet_map_id` (`account_vnet_map_id`) REFERENCES `account_vnet_map` (`id`); - + update `cloud`.`vm_template` set state='Allocated' where state is NULL; update `cloud`.`vm_template` set update_count=0 where update_count is NULL; @@ -2100,7 +2100,7 @@ CREATE TABLE `cloud`.`vm_disk_statistics` ( CONSTRAINT `fk_vm_disk_statistics__account_id` FOREIGN KEY (`account_id`) REFERENCES `account` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8; -insert into `cloud`.`vm_disk_statistics`(data_center_id,account_id,vm_id,volume_id) +insert into `cloud`.`vm_disk_statistics`(data_center_id,account_id,vm_id,volume_id) select volumes.data_center_id, volumes.account_id, vm_instance.id, volumes.id from volumes,vm_instance where vm_instance.vm_type="User" and vm_instance.state<>"Expunging" and volumes.instance_id=vm_instance.id order by vm_instance.id; DROP TABLE IF EXISTS `cloud`.`ovs_providers`; @@ -2166,7 +2166,7 @@ INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'manag INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'vm.disk.throttling.bytes_write_rate', 0, 'Default disk I/O write rate in bytes per second allowed in User vm\'s disk. '); -- Re-enable foreign key checking, at the end of the upgrade path -SET foreign_key_checks = 1; +SET foreign_key_checks = 1; UPDATE `cloud`.`snapshot_policy` set uuid=id WHERE uuid is NULL; #update shared sg enabled network with not null name in Advance Security Group enabled network @@ -2220,7 +2220,7 @@ CREATE TABLE `cloud`.`external_stratosphere_ssp_credentials` ( DROP VIEW IF EXISTS `cloud`.`project_view`; CREATE VIEW `cloud`.`project_view` AS - select + select projects.id, projects.uuid, projects.name, @@ -2264,7 +2264,7 @@ INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Network', 'DEFAULT', 'manage ALTER TABLE `cloud`.`network_offerings` ADD COLUMN `concurrent_connections` int(10) unsigned COMMENT 'Load Balancer(haproxy) maximum number of concurrent connections(global max)'; - + ALTER TABLE `cloud`.`sync_queue` MODIFY `queue_size` smallint(6) NOT NULL DEFAULT '0' COMMENT 'number of items being processed by the queue'; ALTER TABLE `cloud`.`sync_queue` MODIFY `queue_size_limit` smallint(6) NOT NULL DEFAULT '1' COMMENT 'max number of items the queue can process concurrently'; @@ -2280,7 +2280,7 @@ INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'manag #update the account_vmstats_view - count only user vms DROP VIEW IF EXISTS `cloud`.`account_vmstats_view`; CREATE VIEW `cloud`.`account_vmstats_view` AS - SELECT + SELECT account_id, state, count(*) as vmcount from `cloud`.`vm_instance` @@ -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'); @@ -2329,7 +2329,7 @@ INSERT IGNORE INTO `cloud`.`configuration` VALUES ("Advanced", 'DEFAULT', 'manag DROP VIEW IF EXISTS `cloud`.`data_center_view`; CREATE VIEW `cloud`.`data_center_view` AS - select + select data_center.id, data_center.uuid, data_center.name, diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41310to41400.sql b/engine/schema/src/main/resources/META-INF/db/schema-41310to41400.sql index fbbf0a2aef8a..603e7712ebc0 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41310to41400.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41310to41400.sql @@ -19,7 +19,7 @@ -- Schema upgrade from 4.13.1.0 to 4.14.0.0 --; --- Update the description to indicate this only works with KVM + Ceph +-- Update the description to indicate this only works with KVM + Ceph -- (not implemented properly atm for KVM+NFS/local, and it accidentally works with XS + NFS. Not applicable for VMware) UPDATE `cloud`.`configuration` SET `description`='Indicates whether to always backup primary storage snapshot to secondary storage. Keeping snapshots only on Primary storage is applicable for KVM + Ceph only.' WHERE `name`='snapshot.backup.to.secondary'; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41520to41600.sql b/engine/schema/src/main/resources/META-INF/db/schema-41520to41600.sql index a414f244b089..2464a8a57ce4 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41520to41600.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41520to41600.sql @@ -123,20 +123,6 @@ CREATE VIEW `cloud`.`service_offering_view` AS GROUP BY `service_offering`.`id`; ---; --- Stored procedure to do idempotent column add; --- This is copied from schema-41000to41100.sql ---; -DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_ADD_COLUMN`; - -CREATE PROCEDURE `cloud`.`IDEMPOTENT_ADD_COLUMN` ( - IN in_table_name VARCHAR(200), - IN in_column_name VARCHAR(200), - IN in_column_definition VARCHAR(1000) -) -BEGIN - - DECLARE CONTINUE HANDLER FOR 1060 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', 'ADD COLUMN') ; SET @ddl = CONCAT(@ddl, ' ', in_column_name); SET @ddl = CONCAT(@ddl, ' ', in_column_definition); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.account','created', 'datetime DEFAULT NULL COMMENT ''date created'' AFTER `state` '); CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.domain','created', 'datetime DEFAULT NULL COMMENT ''date created'' AFTER `next_child_seq` '); @@ -730,39 +716,6 @@ ALTER TABLE `cloud`.`annotations` ADD COLUMN `admins_only` tinyint(1) unsigned N -- Add uuid for ssh keypairs ALTER TABLE `cloud`.`ssh_keypairs` ADD COLUMN `uuid` varchar(40) AFTER `id`; --- PR#4699 Drop the procedure `ADD_GUEST_OS_AND_HYPERVISOR_MAPPING` if it already exist. -DROP PROCEDURE IF EXISTS `cloud`.`ADD_GUEST_OS_AND_HYPERVISOR_MAPPING`; - --- PR#4699 Create the procedure `ADD_GUEST_OS_AND_HYPERVISOR_MAPPING` to add guest_os and guest_os_hypervisor mapping. -CREATE PROCEDURE `cloud`.`ADD_GUEST_OS_AND_HYPERVISOR_MAPPING` ( - IN guest_os_category_id bigint(20) unsigned, - IN guest_os_display_name VARCHAR(255), - IN guest_os_hypervisor_hypervisor_type VARCHAR(32), - IN guest_os_hypervisor_hypervisor_version VARCHAR(32), - IN guest_os_hypervisor_guest_os_name VARCHAR(255) -) -BEGIN - INSERT INTO cloud.guest_os (uuid, category_id, display_name, created) - SELECT UUID(), guest_os_category_id, guest_os_display_name, now() - FROM DUAL - WHERE not exists( SELECT 1 - FROM cloud.guest_os - WHERE cloud.guest_os.category_id = guest_os_category_id - AND cloud.guest_os.display_name = guest_os_display_name) - -; INSERT INTO cloud.guest_os_hypervisor (uuid, hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created) - SELECT UUID(), guest_os_hypervisor_hypervisor_type, guest_os_hypervisor_hypervisor_version, guest_os_hypervisor_guest_os_name, guest_os.id, now() - FROM cloud.guest_os - WHERE guest_os.category_id = guest_os_category_id - AND guest_os.display_name = guest_os_display_name - AND NOT EXISTS (SELECT 1 - FROM cloud.guest_os_hypervisor as hypervisor - WHERE hypervisor_type = guest_os_hypervisor_hypervisor_type - AND hypervisor_version = guest_os_hypervisor_hypervisor_version - AND hypervisor.guest_os_id = guest_os.id - AND hypervisor.guest_os_name = guest_os_hypervisor_guest_os_name) -;END; - -- PR#4699 Call procedure `ADD_GUEST_OS_AND_HYPERVISOR_MAPPING` to add new data to guest_os and guest_os_hypervisor. CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (10, 'Ubuntu 20.04 LTS', 'KVM', 'default', 'Ubuntu 20.04 LTS'); CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (10, 'Ubuntu 21.04', 'KVM', 'default', 'Ubuntu 21.04'); diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41600to41610.sql b/engine/schema/src/main/resources/META-INF/db/schema-41600to41610.sql index d53e2181cef8..2a2ae668dae1 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41600to41610.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41600to41610.sql @@ -21,60 +21,6 @@ ALTER TABLE `cloud`.`vm_work_job` ADD COLUMN `secondary_object` char(100) COMMENT 'any additional item that must be checked during queueing' AFTER `vm_instance_id`; --- Stored procedures to handle cloud and cloud_schema changes - --- Idempotent ADD COLUMN -DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_ADD_COLUMN`; -CREATE PROCEDURE `cloud`.`IDEMPOTENT_ADD_COLUMN` ( - IN in_table_name VARCHAR(200) -, IN in_column_name VARCHAR(200) -, IN in_column_definition VARCHAR(1000) -) -BEGIN - DECLARE CONTINUE HANDLER FOR 1060 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', 'ADD COLUMN') ; SET @ddl = CONCAT(@ddl, ' ', in_column_name); SET @ddl = CONCAT(@ddl, ' ', in_column_definition); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; - --- Idempotent ADD COLUMN -DROP PROCEDURE IF EXISTS `cloud_usage`.`IDEMPOTENT_ADD_COLUMN`; -CREATE PROCEDURE `cloud_usage`.`IDEMPOTENT_ADD_COLUMN` ( - IN in_table_name VARCHAR(200) -, IN in_column_name VARCHAR(200) -, IN in_column_definition VARCHAR(1000) -) -BEGIN - DECLARE CONTINUE HANDLER FOR 1060 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', 'ADD COLUMN') ; SET @ddl = CONCAT(@ddl, ' ', in_column_name); SET @ddl = CONCAT(@ddl, ' ', in_column_definition); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; - --- Idempotent DROP INDEX -DROP PROCEDURE IF EXISTS `cloud_usage`.`IDEMPOTENT_DROP_INDEX`; -CREATE PROCEDURE `cloud_usage`.`IDEMPOTENT_DROP_INDEX` ( - IN in_index_name VARCHAR(200) -, IN in_table_name VARCHAR(200) -) -BEGIN - DECLARE CONTINUE HANDLER FOR 1091 BEGIN END; SET @ddl = CONCAT('DROP INDEX ', in_index_name); SET @ddl = CONCAT(@ddl, ' ', ' ON ') ; SET @ddl = CONCAT(@ddl, ' ', in_table_name); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; - --- Idempotent ADD UNIQUE INDEX -DROP PROCEDURE IF EXISTS `cloud_usage`.`IDEMPOTENT_ADD_UNIQUE_INDEX`; -CREATE PROCEDURE `cloud_usage`.`IDEMPOTENT_ADD_UNIQUE_INDEX` ( - IN in_table_name VARCHAR(200) -, IN in_index_name VARCHAR(200) -, IN in_index_definition VARCHAR(1000) -) -BEGIN - DECLARE CONTINUE HANDLER FOR 1061 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', 'ADD UNIQUE INDEX ', in_index_name); SET @ddl = CONCAT(@ddl, ' ', in_index_definition); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; - --- Idempotent CHANGE COLUMN -DROP PROCEDURE IF EXISTS `cloud_usage`.`IDEMPOTENT_CHANGE_COLUMN`; -CREATE PROCEDURE `cloud_usage`.`IDEMPOTENT_CHANGE_COLUMN` ( - IN in_table_name VARCHAR(200) -, IN in_old_column_name VARCHAR(200) -, IN in_new_column_name VARCHAR(200) -, IN in_column_definition VARCHAR(1000) -) -BEGIN - DECLARE CONTINUE HANDLER FOR 1060 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', ' CHANGE COLUMN') ; SET @ddl = CONCAT(@ddl, ' ', in_old_column_name); SET @ddl = CONCAT(@ddl, ' ', in_new_column_name); SET @ddl = CONCAT(@ddl, ' ', in_column_definition); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; - --- Invoke stored procedures to add primary keys on missing tables - -- Add PK to cloud.op_user_stats_log CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.op_user_stats_log', 'id', 'BIGINT(20) NOT NULL AUTO_INCREMENT FIRST, ADD PRIMARY KEY (`id`)'); diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41610to41700.sql b/engine/schema/src/main/resources/META-INF/db/schema-41610to41700.sql index 8417ec296408..ca07c25aaa52 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41610to41700.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41610to41700.sql @@ -219,21 +219,6 @@ CREATE VIEW `cloud`.`service_offering_view` AS `service_offering`.`id`; ---; --- Stored procedure to do idempotent column add; --- This is copied from schema-41000to41100.sql ---; -DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_ADD_COLUMN`; - -CREATE PROCEDURE `cloud`.`IDEMPOTENT_ADD_COLUMN` ( - IN in_table_name VARCHAR(200), - IN in_column_name VARCHAR(200), - IN in_column_definition VARCHAR(1000) -) -BEGIN - - DECLARE CONTINUE HANDLER FOR 1060 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', 'ADD COLUMN') ; SET @ddl = CONCAT(@ddl, ' ', in_column_name); SET @ddl = CONCAT(@ddl, ' ', in_column_definition); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; - CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.volumes','external_uuid', 'VARCHAR(40) DEFAULT null '); DROP VIEW IF EXISTS `cloud`.`volume_view`; @@ -937,35 +922,5 @@ INSERT IGNORE INTO `cloud`.`hypervisor_capabilities`(uuid, hypervisor_type, hype -- Copy XenServer 8.2.0 hypervisor guest OS mappings to XenServer 8.2.1 INSERT IGNORE INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) SELECT UUID(),'Xenserver', '8.2.1', guest_os_name, guest_os_id, utc_timestamp(), 0 FROM `cloud`.`guest_os_hypervisor` WHERE hypervisor_type='Xenserver' AND hypervisor_version='8.2.0'; -DROP PROCEDURE IF EXISTS `cloud`.`ADD_GUEST_OS_AND_HYPERVISOR_MAPPING`; -CREATE PROCEDURE `cloud`.`ADD_GUEST_OS_AND_HYPERVISOR_MAPPING` ( - IN guest_os_category_id bigint(20) unsigned, - IN guest_os_display_name VARCHAR(255), - IN guest_os_hypervisor_hypervisor_type VARCHAR(32), - IN guest_os_hypervisor_hypervisor_version VARCHAR(32), - IN guest_os_hypervisor_guest_os_name VARCHAR(255) - ) -BEGIN -INSERT INTO cloud.guest_os (uuid, category_id, display_name, created) -SELECT UUID(), guest_os_category_id, guest_os_display_name, now() -FROM DUAL -WHERE not exists( SELECT 1 - FROM cloud.guest_os - WHERE cloud.guest_os.category_id = guest_os_category_id - AND cloud.guest_os.display_name = guest_os_display_name) - -; INSERT INTO cloud.guest_os_hypervisor (uuid, hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created) - SELECT UUID(), guest_os_hypervisor_hypervisor_type, guest_os_hypervisor_hypervisor_version, guest_os_hypervisor_guest_os_name, guest_os.id, now() - FROM cloud.guest_os - WHERE guest_os.category_id = guest_os_category_id - AND guest_os.display_name = guest_os_display_name - AND NOT EXISTS (SELECT 1 - FROM cloud.guest_os_hypervisor as hypervisor - WHERE hypervisor_type = guest_os_hypervisor_hypervisor_type - AND hypervisor_version = guest_os_hypervisor_hypervisor_version - AND hypervisor.guest_os_id = guest_os.id - AND hypervisor.guest_os_name = guest_os_hypervisor_guest_os_name) -;END; - CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (2, 'Debian GNU/Linux 11 (64-bit)', 'XenServer', '8.2.1', 'Debian Bullseye 11'); CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (2, 'Debian GNU/Linux 11 (32-bit)', 'XenServer', '8.2.1', 'Debian Bullseye 11'); diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41720to41800.sql b/engine/schema/src/main/resources/META-INF/db/schema-41720to41800.sql index c51d5a43045e..9f38de118197 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41720to41800.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41720to41800.sql @@ -214,16 +214,6 @@ CREATE VIEW `cloud`.`domain_router_view` AS and async_job.instance_type = 'DomainRouter' and async_job.job_status = 0; --- Idempotent ADD COLUMN -DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_ADD_COLUMN`; -CREATE PROCEDURE `cloud`.`IDEMPOTENT_ADD_COLUMN` ( - IN in_table_name VARCHAR(200) -, IN in_column_name VARCHAR(200) -, IN in_column_definition VARCHAR(1000) -) -BEGIN - DECLARE CONTINUE HANDLER FOR 1060 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', 'ADD COLUMN') ; SET @ddl = CONCAT(@ddl, ' ', in_column_name); SET @ddl = CONCAT(@ddl, ' ', in_column_definition); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; - -- Add passphrase table CREATE TABLE IF NOT EXISTS `cloud`.`passphrase` ( `id` bigint unsigned NOT NULL auto_increment, @@ -433,45 +423,6 @@ WHERE roles.role_type != 'Admin' AND roles.is_default = 1 AND role_perm.rule = ' -- VM autoscaling --- Idempotent ADD COLUMN -DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_ADD_COLUMN`; -CREATE PROCEDURE `cloud`.`IDEMPOTENT_ADD_COLUMN` ( - IN in_table_name VARCHAR(200) -, IN in_column_name VARCHAR(200) -, IN in_column_definition VARCHAR(1000) -) -BEGIN - DECLARE CONTINUE HANDLER FOR 1060 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', 'ADD COLUMN') ; SET @ddl = CONCAT(@ddl, ' ', in_column_name); SET @ddl = CONCAT(@ddl, ' ', in_column_definition); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; - --- Idempotent RENAME COLUMN -DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_CHANGE_COLUMN`; -CREATE PROCEDURE `cloud`.`IDEMPOTENT_CHANGE_COLUMN` ( - IN in_table_name VARCHAR(200) -, IN in_column_name VARCHAR(200) -, IN in_column_new_name VARCHAR(200) -, IN in_column_new_definition VARCHAR(1000) -) -BEGIN - DECLARE CONTINUE HANDLER FOR 1054 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', 'CHANGE COLUMN') ; SET @ddl = CONCAT(@ddl, ' ', in_column_name); SET @ddl = CONCAT(@ddl, ' ', in_column_new_name); SET @ddl = CONCAT(@ddl, ' ', in_column_new_definition); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; - --- Idempotent ADD UNIQUE KEY -DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_ADD_UNIQUE_KEY`; -CREATE PROCEDURE `cloud`.`IDEMPOTENT_ADD_UNIQUE_KEY` ( - IN in_table_name VARCHAR(200) -, IN in_key_name VARCHAR(200) -, IN in_key_definition VARCHAR(1000) -) -BEGIN - DECLARE CONTINUE HANDLER FOR 1061 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', 'ADD UNIQUE KEY ', in_key_name); SET @ddl = CONCAT(@ddl, ' ', in_key_definition); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; - --- Idempotent DROP FOREIGN KEY -DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_DROP_FOREIGN_KEY`; -CREATE PROCEDURE `cloud`.`IDEMPOTENT_DROP_FOREIGN_KEY` ( - IN in_table_name VARCHAR(200) -, IN in_foreign_key_name VARCHAR(200) -) -BEGIN - DECLARE CONTINUE HANDLER FOR 1091, 1025 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', ' DROP FOREIGN KEY '); SET @ddl = CONCAT(@ddl, ' ', in_foreign_key_name); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; -- Add column 'supports_vm_autoscaling' to 'network_offerings' table CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.network_offerings', 'supports_vm_autoscaling', 'boolean default false'); @@ -1186,16 +1137,6 @@ CREATE TABLE IF NOT EXISTS `cloud`.`tungsten_lb_health_monitor` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8; --- #6888 add index to speed up querying IPs in the network-tab -DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_ADD_KEY`; - -CREATE PROCEDURE `cloud`.`IDEMPOTENT_ADD_KEY` ( - IN in_index_name VARCHAR(200) - , IN in_table_name VARCHAR(200) - , IN in_key_definition VARCHAR(1000) -) -BEGIN - - DECLARE CONTINUE HANDLER FOR 1061 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', ' ADD KEY ') ; SET @ddl = CONCAT(@ddl, ' ', in_index_name); SET @ddl = CONCAT(@ddl, ' ', in_key_definition); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; CALL `cloud`.`IDEMPOTENT_ADD_KEY`('i_user_ip_address_state','user_ip_address', '(state)'); diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41900to41910-cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-41900to41910-cleanup.sql new file mode 100644 index 000000000000..2d57db2b778e --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/schema-41900to41910-cleanup.sql @@ -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. + +--; +-- Schema upgrade cleanup from 4.19.0.0 to 4.19.1.0 +--; + +-- List VMs response optimisation, don't sum during API handling +UPDATE cloud.configuration set value='false' where name='vm.stats.increment.metrics'; +DELETE from cloud.configuration where name='vm.stats.increment.metrics.in.memory'; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41900to41910.sql b/engine/schema/src/main/resources/META-INF/db/schema-41900to41910.sql new file mode 100644 index 000000000000..0cb10f4a0ef9 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/schema-41900to41910.sql @@ -0,0 +1,72 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +--; +-- Schema upgrade from 4.19.0.0 to 4.19.1.0 +--; + +-- Updates the populated Quota tariff's types VM_DISK_BYTES_READ, VM_DISK_BYTES_WRITE, VM_DISK_IO_READ and VM_DISK_IO_WRITE to the correct unit. + +UPDATE cloud_usage.quota_tariff +SET usage_unit = 'Bytes', updated_on = NOW() +WHERE effective_on = '2010-05-04 00:00:00' +AND name IN ('VM_DISK_BYTES_READ', 'VM_DISK_BYTES_WRITE'); + +UPDATE cloud_usage.quota_tariff +SET usage_unit = 'IOPS', updated_on = NOW() +WHERE effective_on = '2010-05-04 00:00:00' +AND name IN ('VM_DISK_IO_READ', 'VM_DISK_IO_WRITE'); + +-- PR #7236 - [Usage] Create network billing +CREATE TABLE IF NOT EXISTS `cloud_usage`.`usage_networks` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `network_offering_id` bigint(20) unsigned NOT NULL, + `zone_id` bigint(20) unsigned NOT NULL, + `network_id` bigint(20) unsigned NOT NULL, + `account_id` bigint(20) unsigned NOT NULL, + `domain_id` bigint(20) unsigned NOT NULL, + `state` varchar(100) DEFAULT NULL, + `removed` datetime DEFAULT NULL, + `created` datetime NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB CHARSET=utf8; + +-- allow for bigger urls + +ALTER TABLE `cloud`.`vm_template` MODIFY COLUMN `url` VARCHAR(1024) DEFAULT NULL COMMENT 'the url where the template exists externally'; + +-- PR #7235 - [Usage] Create VPC billing +CREATE TABLE IF NOT EXISTS `cloud_usage`.`usage_vpc` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `vpc_id` bigint(20) unsigned NOT NULL, + `zone_id` bigint(20) unsigned NOT NULL, + `account_id` bigint(20) unsigned NOT NULL, + `domain_id` bigint(20) unsigned NOT NULL, + `state` varchar(100) DEFAULT NULL, + `created` datetime NOT NULL, + `removed` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB CHARSET=utf8; + +CALL `cloud_usage`.`IDEMPOTENT_ADD_COLUMN`('cloud_usage.cloud_usage', 'state', 'VARCHAR(100) DEFAULT NULL'); + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.user_data', 'removed', 'datetime COMMENT "date removed or null, if still present"'); + +-- Update options for config - host.allocators.order +UPDATE `cloud`.`configuration` SET + `options` = 'FirstFitRouting,RandomAllocator,TestingAllocator,FirstFitAllocator,RecreateHostAllocator' +WHERE `name` = 'host.allocators.order'; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql b/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql deleted file mode 100644 index 1c368a2fbee2..000000000000 --- a/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql +++ /dev/null @@ -1,20 +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. - ---; --- Schema upgrade from 4.19.0.0 to 4.20.0.0 ---; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41910to41920-cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-41910to41920-cleanup.sql new file mode 100644 index 000000000000..cb317c69b796 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/schema-41910to41920-cleanup.sql @@ -0,0 +1,23 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +--; +-- Schema upgrade cleanup from 4.19.1.0 to 4.19.2.0 +--; + +-- Delete `project_account` entries for users that were removed +DELETE FROM `cloud`.`project_account` WHERE `user_id` IN (SELECT `id` FROM `cloud`.`user` WHERE `removed`); diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41910to41920.sql b/engine/schema/src/main/resources/META-INF/db/schema-41910to41920.sql new file mode 100644 index 000000000000..12ead739d848 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/schema-41910to41920.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. + +--; +-- Schema upgrade from 4.19.1.0 to 4.19.2.0 +--; + +-- Add last_id to the volumes table +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.volumes', 'last_id', 'bigint(20) unsigned DEFAULT NULL'); + +-- Grant access to 2FA APIs for the "Read-Only User - Default" role + +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Read-Only User - Default', 'setupUserTwoFactorAuthentication', 'ALLOW'); +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Read-Only User - Default', 'validateUserTwoFactorAuthenticationCode', 'ALLOW'); +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Read-Only User - Default', 'listUserTwoFactorAuthenticatorProviders', 'ALLOW'); + +-- Grant access to 2FA APIs for the "Support User - Default" role + +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support User - Default', 'setupUserTwoFactorAuthentication', 'ALLOW'); +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support User - Default', 'validateUserTwoFactorAuthenticationCode', 'ALLOW'); +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support User - Default', 'listUserTwoFactorAuthenticatorProviders', 'ALLOW'); + +-- Grant access to 2FA APIs for the "Read-Only Admin - Default" role + +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Read-Only Admin - Default', 'setupUserTwoFactorAuthentication', 'ALLOW'); +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Read-Only Admin - Default', 'validateUserTwoFactorAuthenticationCode', 'ALLOW'); + +-- Grant access to 2FA APIs for the "Support Admin - Default" role + +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support Admin - Default', 'setupUserTwoFactorAuthentication', 'ALLOW'); +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support Admin - Default', 'validateUserTwoFactorAuthenticationCode', 'ALLOW'); diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41900to42000-cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-41910to42000-cleanup.sql similarity index 100% rename from engine/schema/src/main/resources/META-INF/db/schema-41900to42000-cleanup.sql rename to engine/schema/src/main/resources/META-INF/db/schema-41910to42000-cleanup.sql diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql b/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql new file mode 100644 index 000000000000..eec4ac3f0280 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql @@ -0,0 +1,427 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +--; +-- Schema upgrade from 4.19.0.0 to 4.20.0.0 +--; + +-- Add tag column to tables +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.resource_limit', 'tag', 'varchar(64) DEFAULT NULL COMMENT "tag for the limit" '); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.resource_count', 'tag', 'varchar(64) DEFAULT NULL COMMENT "tag for the resource count" '); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.resource_reservation', 'tag', 'varchar(64) DEFAULT NULL COMMENT "tag for the resource reservation" '); +CALL `cloud`.`IDEMPOTENT_DROP_INDEX`('i_resource_count__type_accountId', 'cloud.resource_count'); +CALL `cloud`.`IDEMPOTENT_DROP_INDEX`('i_resource_count__type_domaintId', 'cloud.resource_count'); + +DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_ADD_UNIQUE_INDEX`; +CREATE PROCEDURE `cloud`.`IDEMPOTENT_ADD_UNIQUE_INDEX` ( + IN in_table_name VARCHAR(200), + IN in_index_name VARCHAR(200), + IN in_index_definition VARCHAR(1000) +) +BEGIN + DECLARE CONTINUE HANDLER FOR 1061 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name, ' ', 'ADD UNIQUE INDEX ', in_index_name, ' ', in_index_definition); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; + +CALL `cloud`.`IDEMPOTENT_ADD_UNIQUE_INDEX`('cloud.resource_count', 'i_resource_count__type_tag_accountId', '(type, tag, account_id)'); +CALL `cloud`.`IDEMPOTENT_ADD_UNIQUE_INDEX`('cloud.resource_count', 'i_resource_count__type_tag_domainId', '(type, tag, domain_id)'); + +ALTER TABLE `cloud`.`resource_reservation` + MODIFY COLUMN `amount` bigint NOT NULL; + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.resource_reservation', 'resource_id', 'bigint unsigned NULL COMMENT "id of the resource" '); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.resource_reservation', 'mgmt_server_id', 'bigint unsigned NULL COMMENT "management server id" '); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.resource_reservation', 'created', 'datetime DEFAULT NULL COMMENT "date when the reservation was created" '); + +UPDATE `cloud`.`resource_reservation` SET `created` = now() WHERE created IS NULL; + + +-- Update Default System offering for Router to 512MiB +UPDATE `cloud`.`service_offering` SET ram_size = 512 WHERE unique_name IN ("Cloud.Com-SoftwareRouter", "Cloud.Com-SoftwareRouter-Local", + "Cloud.Com-InternalLBVm", "Cloud.Com-InternalLBVm-Local", + "Cloud.Com-ElasticLBVm", "Cloud.Com-ElasticLBVm-Local") + AND system_use = 1 AND ram_size < 512; + +-- NSX Plugin -- +CREATE TABLE IF NOT EXISTS `cloud`.`nsx_providers` ( + `id` bigint unsigned NOT NULL auto_increment COMMENT 'id', + `uuid` varchar(40), + `zone_id` bigint unsigned NOT NULL COMMENT 'Zone ID', + `host_id` bigint unsigned NOT NULL COMMENT 'Host ID', + `provider_name` varchar(40), + `hostname` varchar(255) NOT NULL, + `port` varchar(255), + `username` varchar(255) NOT NULL, + `password` varchar(255) NOT NULL, + `tier0_gateway` varchar(255), + `edge_cluster` varchar(255), + `transport_zone` varchar(255), + `created` datetime NOT NULL COMMENT 'date created', + `removed` datetime COMMENT 'date removed if not null', + PRIMARY KEY (`id`), + CONSTRAINT `fk_nsx_providers__zone_id` FOREIGN KEY `fk_nsx_providers__zone_id` (`zone_id`) REFERENCES `data_center`(`id`) ON DELETE CASCADE, + INDEX `i_nsx_providers__zone_id`(`zone_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- NSX Plugin -- +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.network_offerings','for_nsx', 'int(1) unsigned DEFAULT "0" COMMENT "is nsx enabled for the resource"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.network_offerings','nsx_mode', 'varchar(32) COMMENT "mode in which the network would route traffic"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','for_nsx', 'int(1) unsigned DEFAULT "0" COMMENT "is nsx enabled for the resource"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','nsx_mode', 'varchar(32) COMMENT "mode in which the network would route traffic"'); + +-- Create table to persist quota email template configurations +CREATE TABLE IF NOT EXISTS `cloud_usage`.`quota_email_configuration`( + `account_id` int(11) NOT NULL, + `email_template_id` bigint(20) NOT NULL, + `enabled` int(1) UNSIGNED NOT NULL, + PRIMARY KEY (`account_id`, `email_template_id`), + CONSTRAINT `FK_quota_email_configuration_account_id` FOREIGN KEY (`account_id`) REFERENCES `cloud_usage`.`quota_account`(`account_id`), + CONSTRAINT `FK_quota_email_configuration_email_template_id` FOREIGN KEY (`email_template_id`) REFERENCES `cloud_usage`.`quota_email_templates`(`id`)); + +-- Remove on delete cascade from snapshot schedule +ALTER TABLE `cloud`.`snapshot_schedule` DROP CONSTRAINT `fk__snapshot_schedule_async_job_id`; + +-- Add `is_implicit` column to `host_tags` table +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.host_tags', 'is_implicit', 'int(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT "If host tag is implicit or explicit" '); + +-- Fields related to Snapshot Extraction +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.snapshot_store_ref', 'download_url', 'varchar(2048) DEFAULT NULL'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.snapshot_store_ref', 'download_url_created', 'datetime DEFAULT NULL'); + +-- Webhooks feature +DROP TABLE IF EXISTS `cloud`.`webhook`; +CREATE TABLE `cloud`.`webhook` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id of the webhook', + `uuid` varchar(255) COMMENT 'uuid of the webhook', + `name` varchar(255) NOT NULL COMMENT 'name of the webhook', + `description` varchar(4096) COMMENT 'description for the webhook', + `state` char(32) NOT NULL COMMENT 'state of the webhook - Enabled or Disabled', + `domain_id` bigint unsigned NOT NULL COMMENT 'id of the owner domain of the webhook', + `account_id` bigint unsigned NOT NULL COMMENT 'id of the owner account of the webhook', + `payload_url` varchar(255) COMMENT 'payload URL for the webhook', + `secret_key` varchar(255) COMMENT 'secret key for the webhook', + `ssl_verification` boolean COMMENT 'for https payload url, if true then strict ssl verification', + `scope` char(32) NOT NULL COMMENT 'scope for the webhook - Local, Domain, Global', + `created` datetime COMMENT 'date the webhook was created', + `removed` datetime COMMENT 'date removed if not null', + PRIMARY KEY(`id`), + INDEX `i_webhook__account_id`(`account_id`), + CONSTRAINT `fk_webhook__account_id` FOREIGN KEY (`account_id`) REFERENCES `account`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `cloud`.`webhook_delivery`; +CREATE TABLE `cloud`.`webhook_delivery` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id of the webhook delivery', + `uuid` varchar(255) COMMENT 'uuid of the webhook', + `event_id` bigint unsigned NOT NULL COMMENT 'id of the event', + `webhook_id` bigint unsigned NOT NULL COMMENT 'id of the webhook', + `mshost_msid` bigint unsigned NOT NULL COMMENT 'msid of the management server', + `headers` TEXT COMMENT 'headers for the webhook delivery', + `payload` TEXT COMMENT 'payload for the webhook delivery', + `success` boolean COMMENT 'webhook delivery succeeded or not', + `response` TEXT COMMENT 'response of the webhook delivery', + `start_time` datetime COMMENT 'start timestamp of the webhook delivery', + `end_time` datetime COMMENT 'end timestamp of the webhook delivery', + PRIMARY KEY(`id`), + INDEX `i_webhook__event_id`(`event_id`), + INDEX `i_webhook__webhook_id`(`webhook_id`), + CONSTRAINT `fk_webhook__event_id` FOREIGN KEY (`event_id`) REFERENCES `event`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_webhook__webhook_id` FOREIGN KEY (`webhook_id`) REFERENCES `webhook`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- Normalize quota.usage.smtp.useStartTLS, quota.usage.smtp.useAuth, alert.smtp.useAuth and project.smtp.useAuth values +UPDATE + `cloud`.`configuration` +SET + value = "true" +WHERE + name IN ("quota.usage.smtp.useStartTLS", "quota.usage.smtp.useAuth", "alert.smtp.useAuth", "project.smtp.useAuth") + AND value IN ("true", "y", "t", "1", "on", "yes"); + +UPDATE + `cloud`.`configuration` +SET + value = "false" +WHERE + name IN ("quota.usage.smtp.useStartTLS", "quota.usage.smtp.useAuth", "alert.smtp.useAuth", "project.smtp.useAuth") + AND value NOT IN ("true", "y", "t", "1", "on", "yes"); + +-- Create tables for static and dynamic routing +CREATE TABLE `cloud`.`dc_ip4_guest_subnets` ( + `id` bigint unsigned NOT NULL auto_increment COMMENT 'id', + `uuid` varchar(40) DEFAULT NULL, + `data_center_id` bigint(20) unsigned NOT NULL COMMENT 'zone it belongs to', + `subnet` varchar(255) NOT NULL COMMENT 'subnet of the ip4 network', + `domain_id` bigint unsigned DEFAULT NULL COMMENT 'domain the subnet belongs to', + `account_id` bigint unsigned DEFAULT NULL COMMENT 'owner of this subnet', + `created` datetime DEFAULT NULL, + `removed` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + CONSTRAINT `fk_dc_ip4_guest_subnets__data_center_id` FOREIGN KEY (`data_center_id`) REFERENCES `data_center`(`id`), + CONSTRAINT `fk_dc_ip4_guest_subnets__domain_id` FOREIGN KEY (`domain_id`) REFERENCES `domain`(`id`), + CONSTRAINT `fk_dc_ip4_guest_subnets__account_id` FOREIGN KEY (`account_id`) REFERENCES `account`(`id`), + CONSTRAINT `uc_dc_ip4_guest_subnets__uuid` UNIQUE (`uuid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud`.`ip4_guest_subnet_network_map` ( + `id` bigint unsigned NOT NULL auto_increment COMMENT 'id', + `uuid` varchar(40) DEFAULT NULL, + `parent_id` bigint(20) unsigned COMMENT 'ip4 guest subnet which subnet belongs to', + `subnet` varchar(255) NOT NULL COMMENT 'subnet of the ip4 network', + `network_id` bigint(20) unsigned DEFAULT NULL COMMENT 'network which subnet is associated to', + `vpc_id` bigint(20) unsigned DEFAULT NULL COMMENT 'VPC which subnet is associated to', + `state` varchar(255) NOT NULL COMMENT 'state of the subnet', + `allocated` datetime DEFAULT NULL, + `created` datetime DEFAULT NULL, + `removed` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + CONSTRAINT `fk_ip4_guest_subnet_network_map__parent_id` FOREIGN KEY (`parent_id`) REFERENCES `dc_ip4_guest_subnets`(`id`), + CONSTRAINT `fk_ip4_guest_subnet_network_map__network_id` FOREIGN KEY (`network_id`) REFERENCES `networks`(`id`), + CONSTRAINT `fk_ip4_guest_subnet_network_map__vpc_id` FOREIGN KEY (`vpc_id`) REFERENCES `vpc`(`id`), + CONSTRAINT `uc_ip4_guest_subnet_network_map__uuid` UNIQUE (`uuid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CALL `cloud`.`IDEMPOTENT_CHANGE_COLUMN`('network_offerings', 'nsx_mode', 'network_mode', 'varchar(32) COMMENT "mode in which the network would route traffic"'); +CALL `cloud`.`IDEMPOTENT_CHANGE_COLUMN`('vpc_offerings', 'nsx_mode', 'network_mode', 'varchar(32) COMMENT "mode in which the network would route traffic"'); +ALTER TABLE `cloud`.`event` MODIFY COLUMN `type` varchar(50) NOT NULL; + +-- Add tables for AS Numbers and range +CREATE TABLE IF NOT EXISTS `cloud`.`as_number_range` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `uuid` varchar(40) DEFAULT NULL, + `data_center_id` bigint unsigned NOT NULL COMMENT 'zone that it belongs to', + `start_as_number` bigint unsigned NOT NULL COMMENT 'start AS number of the range', + `end_as_number` bigint unsigned NOT NULL COMMENT 'end AS number of the range', + `created` datetime DEFAULT NULL COMMENT 'date created', + `removed` datetime DEFAULT NULL COMMENT 'date removed', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_as_number_range__uuid` (`uuid`), + UNIQUE KEY `uk_as_number_range__range` (`data_center_id`,`start_as_number`,`end_as_number`, `removed`), + CONSTRAINT `fk_as_number_range__data_center_id` FOREIGN KEY (`data_center_id`) REFERENCES `data_center` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cloud`.`as_number` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `uuid` varchar(40) DEFAULT NULL, + `account_id` bigint unsigned DEFAULT NULL, + `domain_id` bigint unsigned DEFAULT NULL, + `as_number` bigint unsigned NOT NULL COMMENT 'the AS Number', + `as_number_range_id` bigint unsigned NOT NULL, + `data_center_id` bigint unsigned NOT NULL COMMENT 'zone that it belongs to', + `allocated` datetime DEFAULT NULL COMMENT 'Date this AS Number was allocated to some network', + `is_allocated` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'indicates if the AS Number is allocated to some network', + `network_id` bigint unsigned DEFAULT NULL COMMENT 'Network this AS Number is associated with', + `vpc_id` bigint unsigned DEFAULT NULL COMMENT 'VPC this AS Number is associated with', + `created` datetime DEFAULT NULL COMMENT 'date created', + `removed` datetime DEFAULT NULL COMMENT 'date removed', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_as_number__uuid` (`uuid`), + UNIQUE KEY `uk_as_number__number` (`data_center_id`,`as_number`,`as_number_range_id`), + CONSTRAINT `fk_as_number__account_id` FOREIGN KEY (`account_id`) REFERENCES `account` (`id`), + CONSTRAINT `fk_as_number__data_center_id` FOREIGN KEY (`data_center_id`) REFERENCES `data_center` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_as_number__network_id` FOREIGN KEY (`network_id`) REFERENCES `networks` (`id`), + CONSTRAINT `fk_as_number__as_number_range_id` FOREIGN KEY (`as_number_range_id`) REFERENCES `as_number_range` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.network_offerings','routing_mode', 'varchar(10) COMMENT "routing mode for the offering"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.network_offerings','specify_as_number', 'tinyint(1) NOT NULL DEFAULT 0 COMMENT "specify AS number when using dynamic routing"'); + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','routing_mode', 'varchar(10) COMMENT "routing mode for the offering"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','specify_as_number', 'tinyint(1) NOT NULL DEFAULT 0 COMMENT "specify AS number when using dynamic routing"'); + +-- Tables for Dynamic Routing +CREATE TABLE IF NOT EXISTS `cloud`.`bgp_peers` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `uuid` varchar(40) DEFAULT NULL, + `data_center_id` bigint(20) unsigned NOT NULL COMMENT 'zone it belongs to', + `ip4_address` varchar(40) DEFAULT NULL COMMENT 'IPv4 address of the BGP peer', + `ip6_address` varchar(40) DEFAULT NULL COMMENT 'IPv6 address of the BGP peer', + `as_number` bigint unsigned NOT NULL COMMENT 'AS number of the BGP peer', + `password` varchar(255) DEFAULT NULL COMMENT 'Password of the BGP peer', + `domain_id` bigint unsigned DEFAULT NULL COMMENT 'domain the subnet belongs to', + `account_id` bigint unsigned DEFAULT NULL COMMENT 'owner of this subnet', + `created` datetime DEFAULT NULL COMMENT 'date created', + `removed` datetime DEFAULT NULL COMMENT 'date removed', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_bgp_peers__uuid` (`uuid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud`.`bgp_peer_details` ( + `id` bigint unsigned NOT NULL auto_increment, + `bgp_peer_id` bigint unsigned NOT NULL COMMENT 'bgp peer id', + `name` varchar(255) NOT NULL, + `value` varchar(1024) 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_bgp_peer_details__bgp_peer_id` FOREIGN KEY `fk_bgp_peer_details__bgp_peer_id`(`bgp_peer_id`) REFERENCES `bgp_peers`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cloud`.`bgp_peer_network_map` ( + `id` bigint unsigned NOT NULL auto_increment COMMENT 'id', + `bgp_peer_id` bigint(20) unsigned COMMENT 'id of the BGP peer', + `network_id` bigint(20) unsigned DEFAULT NULL COMMENT 'network which BGP peer is associated to', + `vpc_id` bigint(20) unsigned DEFAULT NULL COMMENT 'vpc which BGP peer is associated to', + `state` varchar(40) DEFAULT NULL, + `created` datetime DEFAULT NULL COMMENT 'date created', + `removed` datetime DEFAULT NULL COMMENT 'date removed', + PRIMARY KEY (`id`), + CONSTRAINT `fk_bgp_peer_network_map__bgp_peer_id` FOREIGN KEY (`bgp_peer_id`) REFERENCES `bgp_peers`(`id`), + CONSTRAINT `fk_bgp_peer_network_map__network_id` FOREIGN KEY (`network_id`) REFERENCES `networks`(`id`), + CONSTRAINT `fk_bgp_peer_network_map__vpc_id` FOREIGN KEY (`vpc_id`) REFERENCES `vpc`(`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud`.`shared_filesystem`( + `id` bigint unsigned NOT NULL auto_increment COMMENT 'ID', + `uuid` varchar(40) COMMENT 'UUID', + `name` varchar(255) NOT NULL COMMENT 'Name of the shared filesystem', + `description` varchar(1024) COMMENT 'Description', + `domain_id` bigint unsigned NOT NULL COMMENT 'Domain ID', + `account_id` bigint unsigned NOT NULL COMMENT 'Account ID', + `data_center_id` bigint unsigned NOT NULL COMMENT 'Data center ID', + `state` varchar(12) NOT NULL COMMENT 'State of the shared filesystem in the FSM', + `fs_provider_name` varchar(255) COMMENT 'Name of the shared filesystem provider', + `protocol` varchar(10) COMMENT 'Protocol supported by the shared filesystem', + `volume_id` bigint unsigned COMMENT 'Volume which the shared filesystem is using as storage', + `vm_id` bigint unsigned COMMENT 'vm on which the shared filesystem is hosted', + `fs_type` varchar(10) NOT NULL COMMENT 'The filesystem format to be used for the shared filesystem', + `service_offering_id` bigint unsigned COMMENT 'Service offering for the vm', + `update_count` bigint unsigned COMMENT 'Update count for state change', + `updated` datetime COMMENT 'date updated', + `created` datetime NOT NULL COMMENT 'date created', + `removed` datetime COMMENT 'date removed if not null', + PRIMARY KEY (`id`), + CONSTRAINT `uc_shared_filesystem__uuid` UNIQUE (`uuid`), + INDEX `i_shared_filesystem__account_id`(`account_id`), + INDEX `i_shared_filesystem__domain_id`(`domain_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- Quota inject tariff result into subsequent ones +CALL `cloud_usage`.`IDEMPOTENT_ADD_COLUMN`('cloud_usage.quota_tariff', 'position', 'bigint(20) NOT NULL DEFAULT 1 COMMENT "Position in the execution sequence for tariffs of the same type"'); + +-- Idempotent IDEMPOTENT_MODIFY_COLUMN_CHAR_SET +DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`; +CREATE PROCEDURE `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET` ( + IN in_table_name VARCHAR(200) +, IN in_column_name VARCHAR(200) +, IN in_column_type VARCHAR(200) +, IN in_column_definition VARCHAR(1000) +) +BEGIN + DECLARE CONTINUE HANDLER FOR 1060 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', ' MODIFY COLUMN') ; SET @ddl = CONCAT(@ddl, ' ', in_column_name); SET @ddl = CONCAT(@ddl, ' ', in_column_type); SET @ddl = CONCAT(@ddl, ' ', ' CHARACTER SET utf8mb4'); SET @ddl = CONCAT(@ddl, ' ', in_column_definition); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; + +DROP PROCEDURE IF EXISTS `cloud_usage`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`; +CREATE PROCEDURE `cloud_usage`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET` ( + IN in_table_name VARCHAR(200) +, IN in_column_name VARCHAR(200) +, IN in_column_type VARCHAR(200) +, IN in_column_definition VARCHAR(1000) +) +BEGIN + DECLARE CONTINUE HANDLER FOR 1060 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', ' MODIFY COLUMN') ; SET @ddl = CONCAT(@ddl, ' ', in_column_name); SET @ddl = CONCAT(@ddl, ' ', in_column_type); SET @ddl = CONCAT(@ddl, ' ', ' CHARACTER SET utf8mb4'); SET @ddl = CONCAT(@ddl, ' ', in_column_definition); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; + +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('async_job', 'job_result', 'TEXT', 'COMMENT \'job result info\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('async_job', 'job_cmd_info', 'TEXT', 'COMMENT \'command parameter info\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('event', 'description', 'VARCHAR(1024)', 'NOT NULL'); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('usage_event', 'resource_name', 'VARCHAR(255)', 'DEFAULT NULL'); +CALL `cloud_usage`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('usage_event', 'resource_name', 'VARCHAR(255)', 'DEFAULT NULL'); + +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('account', 'account_name', 'VARCHAR(100)', 'DEFAULT NULL COMMENT \'an account name set by the creator of the account, defaults to username for single accounts\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('affinity_group', 'description', 'VARCHAR(4096)', 'DEFAULT NULL'); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('annotations', 'annotation', 'TEXT', ''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('autoscale_vmgroups', 'name', 'VARCHAR(255)', 'DEFAULT NULL COMMENT \'name of the autoscale vm group\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('backup_offering', 'name', 'VARCHAR(255)', 'NOT NULL COMMENT \'backup offering name\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('backup_offering', 'description', 'VARCHAR(255)', 'NOT NULL COMMENT \'backup offering description\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('disk_offering', 'name', 'VARCHAR(255)', 'NOT NULL'); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('disk_offering', 'unique_name', 'VARCHAR(32)', 'DEFAULT NULL COMMENT \'unique name\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('disk_offering', 'display_text', 'VARCHAR(4096)', 'DEFAULT NULL COMMENT \'Optional text set by the admin for display purpose only\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('instance_group', 'name', 'VARCHAR(255)', 'NOT NULL'); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('kubernetes_cluster', 'name', 'VARCHAR(255)', 'NOT NULL'); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('kubernetes_cluster', 'description', 'VARCHAR(4096)', 'DEFAULT NULL COMMENT \'display text for this Kubernetes cluster\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('kubernetes_supported_version', 'name', 'VARCHAR(255)', 'NOT NULL COMMENT \'the name of this Kubernetes version\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('network_offerings', 'name', 'VARCHAR(64)', 'DEFAULT NULL COMMENT \'name of the network offering\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('network_offerings', 'unique_name', 'VARCHAR(64)', 'DEFAULT NULL COMMENT \'unique name of the network offering\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('network_offerings', 'display_text', 'VARCHAR(255)', 'NOT NULL COMMENT \'text to display to users\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('networks', 'name', 'VARCHAR(255)', 'DEFAULT NULL COMMENT \'name for this network\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('networks', 'display_text', 'VARCHAR(255)', 'DEFAULT NULL COMMENT \'display text for this network\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('project_role', 'description', 'TEXT', 'COMMENT \'description of the project role\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('projects', 'name', 'VARCHAR(255)', 'DEFAULT NULL COMMENT \'project name\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('projects', 'display_text', 'VARCHAR(255)', 'DEFAULT NULL COMMENT \'project display text\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('roles', 'description', 'TEXT', 'COMMENT \'description of the role\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('service_offering', 'name', 'VARCHAR(255)', 'NOT NULL'); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('service_offering', 'unique_name', 'VARCHAR(32)', 'DEFAULT NULL COMMENT \'unique name for offerings\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('service_offering', 'display_text', 'VARCHAR(4096)', 'DEFAULT NULL'); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('snapshots', 'name', 'VARCHAR(255)', 'NOT NULL COMMENT \'snapshot name\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('ssh_keypairs', 'keypair_name', 'VARCHAR(256)', 'NOT NULL COMMENT \'name of the key pair\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('user_vm', 'display_name', 'VARCHAR(255)', 'DEFAULT NULL'); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('user_vm_details', 'value', 'VARCHAR(5120)', 'NOT NULL'); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('user', 'firstname', 'VARCHAR(255)', 'DEFAULT NULL'); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('user', 'lastname', 'VARCHAR(255)', 'DEFAULT NULL'); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('user_data', 'name', 'VARCHAR(256)', 'NOT NULL COMMENT \'name of the user data\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('vm_instance', 'display_name', 'VARCHAR(255)', 'DEFAULT NULL'); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('vm_snapshots', 'display_name', 'VARCHAR(255)', 'DEFAULT NULL'); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('vm_snapshots', 'description', 'VARCHAR(255)', 'DEFAULT NULL'); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('vm_template', 'name', 'VARCHAR(255)', 'NOT NULL'); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('vm_template', 'display_text', 'VARCHAR(4096)', 'DEFAULT NULL COMMENT \'Description text set by the admin for display purpose only\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('volumes', 'name', 'VARCHAR(255)', 'DEFAULT NULL COMMENT \'A user specified name for the volume\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('vpc', 'name', 'VARCHAR(255)', 'DEFAULT NULL COMMENT \'vpc name\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('vpc', 'display_text', 'VARCHAR(255)', 'DEFAULT NULL COMMENT \'vpc display text\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('vpc_offerings', 'name', 'VARCHAR(255)', 'DEFAULT NULL COMMENT \'vpc offering name\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('vpc_offerings', 'unique_name', 'VARCHAR(64)', 'DEFAULT NULL COMMENT \'unique name of the vpc offering\''); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('vpc_offerings', 'display_text', 'VARCHAR(255)', 'DEFAULT NULL COMMENT \'display text\''); + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.roles','state', 'varchar(10) NOT NULL default "enabled" COMMENT "role state"'); + +-- Multi-Arch Zones +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.cluster', 'arch', 'varchar(8) DEFAULT "x86_64" COMMENT "the CPU architecture of the hosts in the cluster"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.host', 'arch', 'varchar(8) DEFAULT "x86_64" COMMENT "the CPU architecture of the host"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vm_template', 'arch', 'varchar(8) DEFAULT "x86_64" COMMENT "the CPU architecture of the template/ISO"'); + +-- NAS B&R Plugin Backup Repository +DROP TABLE IF EXISTS `cloud`.`backup_repository`; +CREATE TABLE `cloud`.`backup_repository` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id of the backup repository', + `uuid` varchar(255) NOT NULL COMMENT 'uuid of the backup repository', + `name` varchar(255) CHARACTER SET utf8mb4 NOT NULL COMMENT 'name of the backup repository', + `zone_id` bigint unsigned NOT NULL COMMENT 'id of zone', + `provider` varchar(255) NOT NULL COMMENT 'backup provider name', + `type` varchar(255) NOT NULL COMMENT 'backup repo type', + `address` varchar(1024) NOT NULL COMMENT 'url of the backup repository', + `mount_opts` varchar(1024) NOT NULL COMMENT 'mount options for the backup repository', + `used_bytes` bigint unsigned, + `capacity_bytes` bigint unsigned, + `created` datetime, + `removed` datetime, + PRIMARY KEY(`id`), + INDEX `i_backup_repository__uuid`(`uuid`), + INDEX `i_backup_repository__zone_id_provider`(`zone_id`, `provider`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- Drop foreign key on backup_schedule, drop unique key on vm_id and re-add foreign key to allow multiple backup schedules to be created +ALTER TABLE `cloud`.`backup_schedule` DROP FOREIGN KEY fk_backup_schedule__vm_id; +ALTER TABLE `cloud`.`backup_schedule` DROP INDEX vm_id; +ALTER TABLE `cloud`.`backup_schedule` ADD CONSTRAINT fk_backup_schedule__vm_id FOREIGN KEY (vm_id) REFERENCES vm_instance(id) ON DELETE CASCADE; + +-- Add volume details to the backups table to keep track of the volumes being backed up +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'backed_volumes', 'text DEFAULT NULL COMMENT "details of backed-up volumes" '); +CALL `cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('backups', 'backed_volumes', 'TEXT', 'DEFAULT NULL COMMENT \'details of backed-up volumes\''); + +-- Add support for VMware 8.0u2 (8.0.2.x) and 8.0u3 (8.0.3.x) +INSERT IGNORE INTO `cloud`.`hypervisor_capabilities` (uuid, hypervisor_type, hypervisor_version, max_guests_limit, security_group_enabled, max_data_volumes_limit, max_hosts_per_cluster, storage_motion_supported, vm_snapshot_enabled) values (UUID(), 'VMware', '8.0.2', 1024, 0, 59, 64, 1, 1); +INSERT IGNORE INTO `cloud`.`guest_os_hypervisor` (uuid, hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) SELECT UUID(),'VMware', '8.0.2', guest_os_name, guest_os_id, utc_timestamp(), 0 FROM `cloud`.`guest_os_hypervisor` WHERE hypervisor_type='VMware' AND hypervisor_version='8.0'; +INSERT IGNORE INTO `cloud`.`hypervisor_capabilities` (uuid, hypervisor_type, hypervisor_version, max_guests_limit, security_group_enabled, max_data_volumes_limit, max_hosts_per_cluster, storage_motion_supported, vm_snapshot_enabled) values (UUID(), 'VMware', '8.0.3', 1024, 0, 59, 64, 1, 1); +INSERT IGNORE INTO `cloud`.`guest_os_hypervisor` (uuid, hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) SELECT UUID(),'VMware', '8.0.3', guest_os_name, guest_os_id, utc_timestamp(), 0 FROM `cloud`.`guest_os_hypervisor` WHERE hypervisor_type='VMware' AND hypervisor_version='8.0'; + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vm_instance', 'delete_protection', 'boolean DEFAULT FALSE COMMENT "delete protection for vm" '); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.volumes', 'delete_protection', 'boolean DEFAULT FALSE COMMENT "delete protection for volumes" '); diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42000to42010-cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-42000to42010-cleanup.sql new file mode 100644 index 000000000000..a00d50a7e10a --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/schema-42000to42010-cleanup.sql @@ -0,0 +1,23 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +--; +-- Schema upgrade cleanup from 4.20.0.0 to 4.20.1.0 +--; + +-- Delete `project_account` entries for users that were removed +DELETE FROM `cloud`.`project_account` WHERE `user_id` IN (SELECT `id` FROM `cloud`.`user` WHERE `removed`); 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 new file mode 100644 index 000000000000..3dd6c18f57c5 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/schema-42000to42010.sql @@ -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. + +--; +-- Schema upgrade from 4.20.0.0 to 4.20.1.0 +--; + +-- Delete user vm details for guest CPU mode/model which are root admin only +DELETE FROM `cloud`.`user_vm_details` WHERE `name` IN ('guest.cpu.mode','guest.cpu.model'); + +-- Delete template details for guest CPU mode/model which are root admin only +DELETE FROM `cloud`.`vm_template_details` WHERE `name` IN ('guest.cpu.mode','guest.cpu.model'); + +-- Add column api_key_access to user and account tables +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.user', 'api_key_access', 'boolean DEFAULT NULL COMMENT "is api key access allowed for the user" AFTER `secret_key`'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.account', 'api_key_access', 'boolean DEFAULT NULL COMMENT "is api key access allowed for the account" '); +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); +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'); + +-- Update the description to indicate this setting applies only to volume snapshots on running instances +UPDATE `cloud`.`configuration` SET `description`='whether volume snapshot is enabled on running instances on KVM hosts' WHERE `name`='kvm.snapshot.enabled'; + +-- Modify index for mshost_peer +DELETE FROM `cloud`.`mshost_peer`; +CALL `cloud`.`IDEMPOTENT_DROP_FOREIGN_KEY`('cloud.mshost_peer','fk_mshost_peer__owner_mshost'); +CALL `cloud`.`IDEMPOTENT_DROP_INDEX`('i_mshost_peer__owner_peer_runid','mshost_peer'); +CALL `cloud`.`IDEMPOTENT_ADD_UNIQUE_KEY`('cloud.mshost_peer', 'i_mshost_peer__owner_peer', '(owner_mshost, peer_mshost)'); +CALL `cloud`.`IDEMPOTENT_ADD_FOREIGN_KEY`('cloud.mshost_peer', 'fk_mshost_peer__owner_mshost', '(owner_mshost)', '`mshost`(`id`)'); + +-- Add last_id to the volumes table +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.volumes', 'last_id', 'bigint(20) unsigned DEFAULT NULL'); + +-- Add used_iops column to support IOPS data in storage stats +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.storage_pool', 'used_iops', 'bigint unsigned DEFAULT NULL COMMENT "IOPS currently in use for this storage pool" '); + +-- Add reason column for op_ha_work +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.op_ha_work', 'reason', 'varchar(32) DEFAULT NULL COMMENT "Reason for the HA work"'); + +-- Support for XCP-ng 8.3.0 and XenServer 8.4 by adding hypervisor capabilities +-- https://docs.xenserver.com/en-us/xenserver/8/system-requirements/configuration-limits.html +-- https://docs.xenserver.com/en-us/citrix-hypervisor/system-requirements/configuration-limits.html +INSERT IGNORE INTO `cloud`.`hypervisor_capabilities`(uuid, hypervisor_type, hypervisor_version, max_guests_limit, max_data_volumes_limit, max_hosts_per_cluster, storage_motion_supported) VALUES (UUID(), 'XenServer', '8.3.0', 1000, 254, 64, 1); +INSERT IGNORE INTO `cloud`.`hypervisor_capabilities`(uuid, hypervisor_type, hypervisor_version, max_guests_limit, max_data_volumes_limit, max_hosts_per_cluster, storage_motion_supported) VALUES (UUID(), 'XenServer', '8.4.0', 1000, 240, 64, 1); + +-- Add missing and new Guest OS mappings +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (2, 'Debian GNU/Linux 10 (64-bit)', 'XenServer', '8.2.1', 'Debian Buster 10'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (5, 'SUSE Linux Enterprise Server 15 (64-bit)', 'XenServer', '8.2.1', 'SUSE Linux Enterprise 15 (64-bit)'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (6, 'Windows Server 2022 (64-bit)', 'XenServer', '8.2.1', 'Windows Server 2022 (64-bit)'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (6, 'Windows 11 (64-bit)', 'XenServer', '8.2.1', 'Windows 11'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (10, 'Ubuntu 20.04 LTS', 'XenServer', '8.2.1', 'Ubuntu Focal Fossa 20.04'); + +-- Copy XS 8.2.1 hypervisor guest OS mappings to XS 8.3 and 8.3 mappings to 8.4 +INSERT IGNORE INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) SELECT UUID(),'Xenserver', '8.3.0', guest_os_name, guest_os_id, utc_timestamp(), 0 FROM `cloud`.`guest_os_hypervisor` WHERE hypervisor_type='Xenserver' AND hypervisor_version='8.2.1'; + +-- Add new and missing guest os mappings for XS 8.3 +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (1, 'Rocky Linux 9', 'XenServer', '8.3.0', 'Rocky Linux 9'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (1, 'Rocky Linux 8', 'XenServer', '8.3.0', 'Rocky Linux 8'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (1, 'AlmaLinux 9', 'XenServer', '8.3.0', 'AlmaLinux 9'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (1, 'AlmaLinux 8', 'XenServer', '8.3.0', 'AlmaLinux 8'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (2, 'Debian GNU/Linux 12 (64-bit)', 'XenServer', '8.3.0', 'Debian Bookworm 12'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (3, 'Oracle Linux 9', 'XenServer', '8.3.0', 'Oracle Linux 9'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (3, 'Oracle Linux 8', 'XenServer', '8.3.0', 'Oracle Linux 8'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (4, 'Red Hat Enterprise Linux 8.0', 'XenServer', '8.3.0', 'Red Hat Enterprise Linux 8'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (4, 'Red Hat Enterprise Linux 9.0', 'XenServer', '8.3.0', 'Red Hat Enterprise Linux 9'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (10, 'Ubuntu 22.04 LTS', 'XenServer', '8.3.0', 'Ubuntu Jammy Jellyfish 22.04'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (5, 'SUSE Linux Enterprise Server 12 SP5 (64-bit)', 'XenServer', '8.3.0', 'SUSE Linux Enterprise Server 12 SP5 (64-bit'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (4, 'NeoKylin Linux Server 7', 'XenServer', '8.3.0', 'NeoKylin Linux Server 7'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (1, 'CentOS Stream 9', 'XenServer', '8.3.0', 'CentOS Stream 9'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (4, 'Scientific Linux 7', 'XenServer', '8.3.0', 'Scientific Linux 7'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (7, 'Generic Linux UEFI', 'XenServer', '8.3.0', 'Generic Linux UEFI'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (7, 'Generic Linux BIOS', 'XenServer', '8.3.0', 'Generic Linux BIOS'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (2, 'Gooroom Platform 2.0', 'XenServer', '8.3.0', 'Gooroom Platform 2.0'); + +INSERT IGNORE INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) SELECT UUID(),'Xenserver', '8.4.0', guest_os_name, guest_os_id, utc_timestamp(), 0 FROM `cloud`.`guest_os_hypervisor` WHERE hypervisor_type='Xenserver' AND hypervisor_version='8.3.0'; + +-- Add new guest os mappings for XS 8.4 and KVM +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (6, 'Windows Server 2025', 'XenServer', '8.4.0', 'Windows Server 2025'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (10, 'Ubuntu 24.04 LTS', 'XenServer', '8.4.0', 'Ubuntu Noble Numbat 24.04'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (2, 'Debian GNU/Linux 10 (64-bit)', 'KVM', 'default', 'Debian GNU/Linux 10 (64-bit)'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (2, 'Debian GNU/Linux 11 (64-bit)', 'KVM', 'default', 'Debian GNU/Linux 11 (64-bit)'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (2, 'Debian GNU/Linux 12 (64-bit)', 'KVM', 'default', 'Debian GNU/Linux 12 (64-bit)'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (6, 'Windows 11 (64-bit)', 'KVM', 'default', 'Windows 11'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (6, 'Windows Server 2025', 'KVM', 'default', 'Windows Server 2025'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (10, 'Ubuntu 24.04 LTS', 'KVM', 'default', 'Ubuntu 24.04 LTS'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (1, 'CentOS Stream 10 (preview)', 'XenServer', '8.4.0', 'CentOS Stream 10 (preview)'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (1, 'CentOS Stream 9', 'XenServer', '8.4.0', 'CentOS Stream 9'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (4, 'Scientific Linux 7', 'XenServer', '8.4.0', 'Scientific Linux 7'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (4, 'NeoKylin Linux Server 7', 'XenServer', '8.4.0', 'NeoKylin Linux Server 7'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (5, 'SUSE Linux Enterprise Server 12 SP5 (64-bit)', 'XenServer', '8.4.0', 'SUSE Linux Enterprise Server 12 SP5 (64-bit'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (2, 'Gooroom Platform 2.0', 'XenServer', '8.4.0', 'Gooroom Platform 2.0'); + +-- Grant access to 2FA APIs for the "Read-Only User - Default" role + +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Read-Only User - Default', 'setupUserTwoFactorAuthentication', 'ALLOW'); +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Read-Only User - Default', 'validateUserTwoFactorAuthenticationCode', 'ALLOW'); +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Read-Only User - Default', 'listUserTwoFactorAuthenticatorProviders', 'ALLOW'); + +-- Grant access to 2FA APIs for the "Support User - Default" role + +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support User - Default', 'setupUserTwoFactorAuthentication', 'ALLOW'); +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support User - Default', 'validateUserTwoFactorAuthenticationCode', 'ALLOW'); +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support User - Default', 'listUserTwoFactorAuthenticatorProviders', 'ALLOW'); + +-- Grant access to 2FA APIs for the "Read-Only Admin - Default" role + +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Read-Only Admin - Default', 'setupUserTwoFactorAuthentication', 'ALLOW'); +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Read-Only Admin - Default', 'validateUserTwoFactorAuthenticationCode', 'ALLOW'); + +-- Grant access to 2FA APIs for the "Support Admin - Default" role + +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support Admin - Default', 'setupUserTwoFactorAuthentication', 'ALLOW'); +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support Admin - Default', 'validateUserTwoFactorAuthenticationCode', 'ALLOW'); + +-- Re-apply VPC: update default network offering for vpc tier to conserve_mode=1 (#8309) +UPDATE `cloud`.`network_offerings` SET conserve_mode=1 WHERE name='DefaultIsolatedNetworkOfferingForVpcNetworks'; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42010to42100-cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-42010to42100-cleanup.sql new file mode 100644 index 000000000000..5f257f2965bd --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/schema-42010to42100-cleanup.sql @@ -0,0 +1,20 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +--; +-- Schema upgrade cleanup from 4.20.1.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-42010to42100.sql new file mode 100644 index 000000000000..4c65f37e0fe0 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql @@ -0,0 +1,759 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +--; +-- Schema upgrade from 4.20.1.0 to 4.21.0.0 +--; + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backup_schedule', 'max_backups', 'INT(8) UNSIGNED NOT NULL DEFAULT 0 COMMENT ''Maximum number of backups to be retained'''); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'backup_schedule_id', 'BIGINT(20) UNSIGNED'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backup_schedule', 'quiescevm', 'tinyint(1) default NULL COMMENT "Quiesce VM before taking backup"'); + +-- Update default value for the config 'vm.network.nic.max.secondary.ipaddresses' (and value to default value if value is null) +UPDATE `cloud`.`configuration` SET default_value = '10' WHERE name = 'vm.network.nic.max.secondary.ipaddresses'; +UPDATE `cloud`.`configuration` SET value = '10' WHERE name = 'vm.network.nic.max.secondary.ipaddresses' AND value IS NULL; + +-- Add console_endpoint_creator_address column to cloud.console_session table +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.console_session', 'console_endpoint_creator_address', 'VARCHAR(45)'); + +-- Add client_address column to cloud.console_session table +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.console_session', 'client_address', 'VARCHAR(45)'); + +-- Allow default roles to use quotaCreditsList +INSERT INTO `cloud`.`role_permissions` (uuid, role_id, rule, permission, sort_order) +SELECT uuid(), role_id, 'quotaCreditsList', permission, sort_order +FROM `cloud`.`role_permissions` rp +WHERE rp.rule = 'quotaStatement' + AND NOT EXISTS(SELECT 1 FROM cloud.role_permissions rp_ WHERE rp.role_id = rp_.role_id AND rp_.rule = 'quotaCreditsList'); + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.host', 'last_mgmt_server_id', 'bigint unsigned DEFAULT NULL COMMENT "last management server this host is connected to" AFTER `mgmt_server_id`'); + +----------------------------------------------------------- +-- CKS Enhancements: +----------------------------------------------------------- +-- Add for_cks column to the vm_template table +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vm_template','for_cks', 'int(1) unsigned DEFAULT "0" COMMENT "if true, the template can be used for CKS cluster deployment"'); + +-- Add support for different node types service offerings on CKS clusters +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','control_node_service_offering_id', 'bigint unsigned COMMENT "service offering ID for Control Node(s)"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','worker_node_service_offering_id', 'bigint unsigned COMMENT "service offering ID for Worker Node(s)"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','etcd_node_service_offering_id', 'bigint unsigned COMMENT "service offering ID for etcd Nodes"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','etcd_node_count', 'bigint unsigned COMMENT "number of etcd nodes to be deployed for the Kubernetes cluster"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','control_node_template_id', 'bigint unsigned COMMENT "template id to be used for Control Node(s)"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','worker_node_template_id', 'bigint unsigned COMMENT "template id to be used for Worker Node(s)"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','etcd_node_template_id', 'bigint unsigned COMMENT "template id to be used for etcd Nodes"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','cni_config_id', 'bigint unsigned COMMENT "user data id representing the associated cni configuration"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','cni_config_details', 'varchar(4096) DEFAULT NULL COMMENT "user data details representing the values required for the cni configuration associated"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster_vm_map','etcd_node', 'tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT "indicates if the VM is an etcd node"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster_vm_map','external_node', 'tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT "indicates if the node was imported into the Kubernetes cluster"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster_vm_map','manual_upgrade', 'tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT "indicates if the node is marked for manual upgrade and excluded from the Kubernetes cluster upgrade operation"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster_vm_map','kubernetes_node_version', 'varchar(40) COMMENT "version of k8s the cluster node is on"'); + +ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__control_node_service_offering_id` FOREIGN KEY `fk_cluster__control_node_service_offering_id`(`control_node_service_offering_id`) REFERENCES `service_offering`(`id`) ON DELETE CASCADE; +ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__worker_node_service_offering_id` FOREIGN KEY `fk_cluster__worker_node_service_offering_id`(`worker_node_service_offering_id`) REFERENCES `service_offering`(`id`) ON DELETE CASCADE; +ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__etcd_node_service_offering_id` FOREIGN KEY `fk_cluster__etcd_node_service_offering_id`(`etcd_node_service_offering_id`) REFERENCES `service_offering`(`id`) ON DELETE CASCADE; +ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__control_node_template_id` FOREIGN KEY `fk_cluster__control_node_template_id`(`control_node_template_id`) REFERENCES `vm_template`(`id`) ON DELETE CASCADE; +ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__worker_node_template_id` FOREIGN KEY `fk_cluster__worker_node_template_id`(`worker_node_template_id`) REFERENCES `vm_template`(`id`) ON DELETE CASCADE; +ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__etcd_node_template_id` FOREIGN KEY `fk_cluster__etcd_node_template_id`(`etcd_node_template_id`) REFERENCES `vm_template`(`id`) ON DELETE CASCADE; + +-- Add for_cks column to the user_data table to represent CNI Configuration stored as userdata +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.user_data','for_cks', 'int(1) unsigned DEFAULT "0" COMMENT "if true, the user data represent CNI configuration meant for CKS use only"'); + +-- Add use VR IP as resolver option on VPC +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc','use_router_ip_resolver', 'tinyint(1) DEFAULT 0 COMMENT "use router ip as resolver instead of dns options"'); +----------------------------------------------------------- +-- END - CKS Enhancements +----------------------------------------------------------- + +-- Add table for reconcile commands +CREATE TABLE IF NOT EXISTS `cloud`.`reconcile_commands` ( + `id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT, + `management_server_id` bigint unsigned NOT NULL COMMENT 'node id of the management server', + `host_id` bigint unsigned NOT NULL COMMENT 'id of the host', + `request_sequence` bigint unsigned NOT NULL COMMENT 'sequence of the request', + `resource_id` bigint unsigned DEFAULT NULL COMMENT 'id of the resource', + `resource_type` varchar(255) COMMENT 'type if the resource', + `state_by_management` varchar(255) COMMENT 'state of the command updated by management server', + `state_by_agent` varchar(255) COMMENT 'state of the command updated by cloudstack agent', + `command_name` varchar(255) COMMENT 'name of the command', + `command_info` MEDIUMTEXT COMMENT 'info of the command', + `answer_name` varchar(255) COMMENT 'name of the answer', + `answer_info` MEDIUMTEXT COMMENT 'info of the answer', + `created` datetime COMMENT 'date the reconcile command was created', + `removed` datetime COMMENT 'date the reconcile command was removed', + `updated` datetime COMMENT 'date the reconcile command was updated', + `retry_count` bigint unsigned DEFAULT 0 COMMENT 'The retry count of reconciliation', + PRIMARY KEY(`id`), + INDEX `i_reconcile_command__host_id`(`host_id`), + CONSTRAINT `fk_reconcile_command__host_id` FOREIGN KEY (`host_id`) REFERENCES `host`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +--- KVM Incremental Snapshots + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.snapshot_store_ref', 'kvm_checkpoint_path', 'varchar(255)'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.snapshot_store_ref', 'end_of_chain', 'int(1) unsigned'); + +-- Create table storage_pool_and_access_group_map +CREATE TABLE IF NOT EXISTS `cloud`.`storage_pool_and_access_group_map` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `pool_id` bigint(20) unsigned NOT NULL COMMENT "pool id", + `storage_access_group` varchar(255) NOT NULL, + PRIMARY KEY (`id`), + KEY `fk_storage_pool_and_access_group_map__pool_id` (`pool_id`), + CONSTRAINT `fk_storage_pool_and_access_group_map__pool_id` FOREIGN KEY (`pool_id`) REFERENCES `storage_pool` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.host', 'storage_access_groups', 'varchar(255) DEFAULT NULL COMMENT "storage access groups for the host"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.cluster', 'storage_access_groups', 'varchar(255) DEFAULT NULL COMMENT "storage access groups for the hosts in the cluster"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.host_pod_ref', 'storage_access_groups', 'varchar(255) DEFAULT NULL COMMENT "storage access groups for the hosts in the pod"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.data_center', 'storage_access_groups', 'varchar(255) DEFAULT NULL COMMENT "storage access groups for the hosts in the zone"'); + +-- Add featured, sort_key, created, removed columns for guest_os_category +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.guest_os_category', 'featured', 'tinyint(1) NOT NULL DEFAULT 0 COMMENT "whether the category is featured or not" AFTER `uuid`'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.guest_os_category', 'sort_key', 'int NOT NULL DEFAULT 0 COMMENT "sort key used for customising sort method" AFTER `featured`'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.guest_os_category', 'created', 'datetime COMMENT "date on which the category was created" AFTER `sort_key`'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.guest_os_category', 'removed', 'datetime COMMENT "date removed if not null" AFTER `created`'); + +-- Begin: Changes for Guest OS category cleanup +-- Add new OS categories if not present +DROP PROCEDURE IF EXISTS `cloud`.`INSERT_CATEGORY_IF_NOT_EXIST`; +CREATE PROCEDURE `cloud`.`INSERT_CATEGORY_IF_NOT_EXIST`(IN os_name VARCHAR(255)) +BEGIN + IF NOT EXISTS ((SELECT 1 FROM `cloud`.`guest_os_category` WHERE name = os_name)) + THEN + INSERT INTO `cloud`.`guest_os_category` (name, uuid) + VALUES (os_name, UUID()) +; END IF +; END; + +CALL `cloud`.`INSERT_CATEGORY_IF_NOT_EXIST`('Fedora'); +CALL `cloud`.`INSERT_CATEGORY_IF_NOT_EXIST`('Rocky Linux'); +CALL `cloud`.`INSERT_CATEGORY_IF_NOT_EXIST`('AlmaLinux'); + +-- Move existing guest OS to new categories +DROP PROCEDURE IF EXISTS `cloud`.`UPDATE_CATEGORY_FOR_GUEST_OSES`; +CREATE PROCEDURE `cloud`.`UPDATE_CATEGORY_FOR_GUEST_OSES`(IN category_name VARCHAR(255), IN os_name VARCHAR(255)) +BEGIN + DECLARE category_id BIGINT +; SELECT `id` INTO category_id + FROM `cloud`.`guest_os_category` + WHERE `name` = category_name + LIMIT 1 +; IF category_id IS NULL THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Category not found' +; END IF +; UPDATE `cloud`.`guest_os` + SET `category_id` = category_id + WHERE `display_name` LIKE CONCAT('%', os_name, '%') +; END; +CALL `cloud`.`UPDATE_CATEGORY_FOR_GUEST_OSES`('Rocky Linux', 'Rocky Linux'); +CALL `cloud`.`UPDATE_CATEGORY_FOR_GUEST_OSES`('AlmaLinux', 'AlmaLinux'); +CALL `cloud`.`UPDATE_CATEGORY_FOR_GUEST_OSES`('Fedora', 'Fedora'); + +-- Move existing guest OS whose category will be deleted to Other category +DROP PROCEDURE IF EXISTS `cloud`.`UPDATE_NEW_AND_DELETE_OLD_CATEGORY_FOR_GUEST_OS`; +CREATE PROCEDURE `cloud`.`UPDATE_NEW_AND_DELETE_OLD_CATEGORY_FOR_GUEST_OS`(IN to_category_name VARCHAR(255), IN from_category_name VARCHAR(255)) +BEGIN + DECLARE done INT DEFAULT 0 +; DECLARE to_category_id BIGINT +; SELECT id INTO to_category_id + FROM `cloud`.`guest_os_category` + WHERE `name` = to_category_name + LIMIT 1 +; IF to_category_id IS NULL THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'ToCategory not found' +; END IF +; UPDATE `cloud`.`guest_os` + SET `category_id` = to_category_id + WHERE `category_id` = (SELECT `id` FROM `cloud`.`guest_os_category` WHERE `name` = from_category_name) +; UPDATE `cloud`.`guest_os_category` SET `removed`=now() WHERE `name` = from_category_name +; END; +CALL `cloud`.`UPDATE_NEW_AND_DELETE_OLD_CATEGORY_FOR_GUEST_OS`('Other', 'Novel'); +CALL `cloud`.`UPDATE_NEW_AND_DELETE_OLD_CATEGORY_FOR_GUEST_OS`('Other', 'None'); +CALL `cloud`.`UPDATE_NEW_AND_DELETE_OLD_CATEGORY_FOR_GUEST_OS`('Other', 'Unix'); +CALL `cloud`.`UPDATE_NEW_AND_DELETE_OLD_CATEGORY_FOR_GUEST_OS`('Other', 'Mac'); + +-- Update featured for existing guest OS categories +UPDATE `cloud`.`guest_os_category` SET featured = 1; + +-- Update sort order for all guest OS categories +UPDATE `cloud`.`guest_os_category` +SET `sort_key` = CASE + WHEN `name` = 'Ubuntu' THEN 1 + WHEN `name` = 'Debian' THEN 2 + WHEN `name` = 'Fedora' THEN 3 + WHEN `name` = 'CentOS' THEN 4 + WHEN `name` = 'Rocky Linux' THEN 5 + WHEN `name` = 'AlmaLinux' THEN 6 + WHEN `name` = 'Oracle' THEN 7 + WHEN `name` = 'RedHat' THEN 8 + WHEN `name` = 'SUSE' THEN 9 + WHEN `name` = 'Windows' THEN 10 + WHEN `name` = 'Other' THEN 11 + ELSE `sort_key` +END; +-- End: Changes for Guest OS category cleanup + +-- Update description for configuration: host.capacityType.to.order.clusters +UPDATE `cloud`.`configuration` SET + `description` = 'The host capacity type (CPU, RAM or COMBINED) is used by deployment planner to order clusters during VM resource allocation' +WHERE `name` = 'host.capacityType.to.order.clusters' + AND `description` = 'The host capacity type (CPU or RAM) is used by deployment planner to order clusters during VM resource allocation'; + +-- Whitelabel GUI +CREATE TABLE IF NOT EXISTS `cloud`.`gui_themes` ( + `id` bigint(20) unsigned NOT NULL auto_increment, + `uuid` varchar(255) UNIQUE, + `name` varchar(2048) NOT NULL COMMENT 'A name to identify the theme.', + `description` varchar(4096) DEFAULT NULL COMMENT 'A description for the theme.', + `css` text DEFAULT NULL COMMENT 'The CSS to be retrieved and imported into the GUI when matching the theme access configurations.', + `json_configuration` text DEFAULT NULL COMMENT 'The JSON with the configurations to be retrieved and imported into the GUI when matching the theme access configurations.', + `recursive_domains` tinyint(1) DEFAULT 0 COMMENT 'Defines whether the subdomains of the informed domains are considered. Default value is false.', + `is_public` tinyint(1) default 1 COMMENT 'Defines whether a theme can be retrieved by anyone when only the `internet_domains_names` is informed. If the `domain_uuids` or `account_uuids` is informed, it is considered as `false`.', + `created` datetime NOT NULL, + `removed` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +); + +CREATE TABLE IF NOT EXISTS `cloud`.`gui_themes_details` ( + `id` bigint(20) unsigned NOT NULL auto_increment, + `gui_theme_id` bigint(20) unsigned NOT NULL COMMENT 'Foreign key referencing the GUI theme on `gui_themes` table.', + `type` varchar(100) NOT NULL COMMENT 'The type of GUI theme details. Valid options are: `account`, `domain` and `commonName`', + `value` text NOT NULL COMMENT 'The value of the `type` details. Can be an UUID (account or domain) or internet common name.', + PRIMARY KEY (`id`), + CONSTRAINT `fk_gui_themes_details__gui_theme_id` FOREIGN KEY (`gui_theme_id`) REFERENCES `gui_themes`(`id`) +); + +-- Create the GPU card table to hold the GPU card information +CREATE TABLE IF NOT EXISTS `cloud`.`gpu_card` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', + `uuid` varchar(40) NOT NULL UNIQUE, + `device_id` varchar(4) NOT NULL COMMENT 'device id of the GPU card', + `device_name` varchar(255) NOT NULL COMMENT 'device name of the GPU card', + `name` varchar(255) NOT NULL COMMENT 'name of the GPU card', + `vendor_name` varchar(255) NOT NULL COMMENT 'vendor name of the GPU card', + `vendor_id` varchar(4) NOT NULL COMMENT 'vendor id of the GPU card', + `created` datetime NOT NULL COMMENT 'date created', + PRIMARY KEY (`id`), + UNIQUE KEY (`vendor_id`, `device_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='GPU cards supported by CloudStack'; + +-- Create the vGPU profile table to hold the vGPU profile information. +CREATE TABLE IF NOT EXISTS `cloud`.`vgpu_profile` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', + `uuid` varchar(40) NOT NULL UNIQUE, + `name` varchar(255) NOT NULL COMMENT 'name of the vGPU profile', + `description` varchar(255) DEFAULT NULL COMMENT 'description of the vGPU profile', + `card_id` bigint unsigned NOT NULL COMMENT 'id of the GPU card', + `video_ram` bigint unsigned DEFAULT NULL COMMENT 'video RAM of the vGPU profile', + `max_heads` bigint unsigned DEFAULT NULL COMMENT 'maximum number of heads of the vGPU profile', + `max_resolution_x` bigint unsigned DEFAULT NULL COMMENT 'maximum resolution x of the vGPU profile', + `max_resolution_y` bigint unsigned DEFAULT NULL COMMENT 'maximum resolution y of the vGPU profile', + `max_vgpu_per_pgpu` bigint unsigned DEFAULT NULL COMMENT 'Maximum number of vGPUs per physical GPU', + `created` datetime NOT NULL COMMENT 'date created', + PRIMARY KEY (`id`), + UNIQUE KEY (`name`, `card_id`), + CONSTRAINT `fk_vgpu_profile_card_id` FOREIGN KEY (`card_id`) REFERENCES `gpu_card`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='vGPU profiles supported by CloudStack'; + +-- Create the GPU device table to hold the GPU device information on different hosts +CREATE TABLE IF NOT EXISTS `cloud`.`gpu_device` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', + `uuid` varchar(40) NOT NULL UNIQUE, + `card_id` bigint unsigned NOT NULL COMMENT 'id of the GPU card', + `vgpu_profile_id` bigint unsigned DEFAULT NULL COMMENT 'id of the vGPU profile.', + `bus_address` varchar(255) NOT NULL COMMENT 'PCI bus address of the GPU device', + `type` varchar(32) NOT NULL COMMENT 'type of the GPU device. PCI or MDEV', + `host_id` bigint unsigned NOT NULL COMMENT 'id of the host where GPU is installed', + `vm_id` bigint unsigned DEFAULT NULL COMMENT 'id of the VM using this GPU device', + `numa_node` varchar(255) DEFAULT NULL COMMENT 'NUMA node of the GPU device', + `pci_root` varchar(255) DEFAULT NULL COMMENT 'PCI root of the GPU device', + `parent_gpu_device_id` bigint unsigned DEFAULT NULL COMMENT 'id of the parent GPU device. null if it is a physical GPU device and for vGPUs points to the actual GPU', + `state` varchar(32) NOT NULL COMMENT 'state of the GPU device', + `managed_state` varchar(32) NOT NULL COMMENT 'resource state of the GPU device', + PRIMARY KEY (`id`), + UNIQUE KEY (`bus_address`, `host_id`), + CONSTRAINT `fk_gpu_devices__card_id` FOREIGN KEY (`card_id`) REFERENCES `gpu_card` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_gpu_devices__host_id` FOREIGN KEY (`host_id`) REFERENCES `host` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_gpu_devices__vm_id` FOREIGN KEY (`vm_id`) REFERENCES `vm_instance` (`id`) ON DELETE SET NULL, + CONSTRAINT `fk_gpu_devices__parent_gpu_device_id` FOREIGN KEY (`parent_gpu_device_id`) REFERENCES `gpu_device` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='GPU devices installed on hosts'; + +-- Add references to GPU tables +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.service_offering', 'vgpu_profile_id', 'bigint unsigned DEFAULT NULL COMMENT "vgpu profile ID"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.service_offering', 'gpu_count', 'int unsigned DEFAULT NULL COMMENT "number of GPUs"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.service_offering', 'gpu_display', 'boolean DEFAULT false COMMENT "enable GPU display"'); +CALL `cloud`.`IDEMPOTENT_DROP_FOREIGN_KEY`('cloud.service_offering','fk_service_offering__vgpu_profile_id'); +CALL `cloud`.`IDEMPOTENT_ADD_FOREIGN_KEY`('cloud.service_offering', 'fk_service_offering__vgpu_profile_id', '(vgpu_profile_id)', '`vgpu_profile`(`id`)'); + +-- Netris Plugin +CREATE TABLE 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', + `host_id` bigint unsigned NOT NULL COMMENT 'Host ID', + `name` varchar(40), + `url` varchar(255) NOT NULL, + `username` varchar(255) NOT NULL, + `password` varchar(255) NOT NULL, + `site_name` varchar(255) NOT NULL, + `tenant_name` varchar(255) NOT NULL, + `netris_tag` varchar(255) NOT NULL, + `created` datetime NOT NULL COMMENT 'created date', + `removed` datetime COMMENT 'removed date if not null', + PRIMARY KEY (`id`), + CONSTRAINT `fk_netris_providers__zone_id` FOREIGN KEY `fk_netris_providers__zone_id` (`zone_id`) REFERENCES `data_center`(`id`) ON DELETE CASCADE, + INDEX `i_netris_providers__zone_id`(`zone_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- Drop the Tungsten and NSX columns from the network offerings (replaced by checking the provider on the ntwk_offering_service_map table) +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) +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`'); + +-- Add `for_router` to `user_ip_address` table +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.user_ip_address', 'for_router', 'tinyint(1) DEFAULT 0 COMMENT "True if the ip address is used by Domain Router to expose services"'); + +-- Add Netris Autoscaling rules +INSERT IGNORE INTO `cloud`.`counter` (uuid, provider, source, name, value, created) VALUES (UUID(), 'Netris', 'cpu', 'VM CPU - average percentage', 'vm.cpu.average.percentage', NOW()); +INSERT IGNORE INTO `cloud`.`counter` (uuid, provider, source, name, value, created) VALUES (UUID(), 'Netris', 'memory', 'VM Memory - average percentage', 'vm.memory.average.percentage', NOW()); + +-- Rename user_vm_details to vm_instance_details +ALTER TABLE `cloud`.`user_vm_details` RENAME TO `cloud`.`vm_instance_details`; +ALTER TABLE `cloud`.`vm_instance_details` DROP FOREIGN KEY `fk_user_vm_details__vm_id`; +ALTER TABLE `cloud`.`vm_instance_details` ADD CONSTRAINT `fk_vm_instance_details__vm_id` FOREIGN KEY (vm_id) REFERENCES vm_instance(id) ON DELETE CASCADE; + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backup_schedule', 'uuid', 'VARCHAR(40) NOT NULL'); +UPDATE `cloud`.`backup_schedule` SET uuid = UUID(); + +-- Extension framework +UPDATE `cloud`.`configuration` SET value = CONCAT(value, ',External') WHERE name = 'hypervisor.list'; + +CREATE TABLE IF NOT EXISTS `cloud`.`extension` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `uuid` varchar(40) NOT NULL UNIQUE, + `name` varchar(255) NOT NULL, + `description` varchar(4096), + `type` varchar(255) NOT NULL COMMENT 'Type of the extension: Orchestrator, etc', + `relative_path` varchar(2048) NOT NULL COMMENT 'Path for the extension relative to the root extensions directory', + `path_ready` tinyint(1) DEFAULT '0' COMMENT 'True if the extension path is in ready state across management servers', + `is_user_defined` tinyint(1) DEFAULT '0' COMMENT 'True if the extension is added by admin', + `state` char(32) NOT NULL COMMENT 'State of the extension - Enabled or Disabled', + `created` datetime NOT NULL, + `removed` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cloud`.`extension_details` ( + `id` bigint unsigned UNIQUE NOT NULL AUTO_INCREMENT COMMENT 'id', + `extension_id` bigint unsigned NOT NULL COMMENT 'extension to which the detail is related to', + `name` varchar(255) NOT NULL COMMENT 'name of the detail', + `value` varchar(255) NOT NULL COMMENT 'value of the detail', + `display` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'True if the detail can be displayed to the end user', + PRIMARY KEY (`id`), + CONSTRAINT `fk_extension_details__extension_id` FOREIGN KEY (`extension_id`) + REFERENCES `extension` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cloud`.`extension_resource_map` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `extension_id` bigint(20) unsigned NOT NULL, + `resource_id` bigint(20) unsigned NOT NULL, + `resource_type` char(255) NOT NULL, + `created` datetime NOT NULL, + `removed` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + CONSTRAINT `fk_extension_resource_map__extension_id` FOREIGN KEY (`extension_id`) + REFERENCES `cloud`.`extension`(`id`) ON DELETE CASCADE, + INDEX `idx_extension_resource` (`resource_id`, `resource_type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cloud`.`extension_resource_map_details` ( + `id` bigint unsigned UNIQUE NOT NULL AUTO_INCREMENT COMMENT 'id', + `extension_resource_map_id` bigint unsigned NOT NULL COMMENT 'mapping to which the detail is related', + `name` varchar(255) NOT NULL COMMENT 'name of the detail', + `value` varchar(255) NOT NULL COMMENT 'value of the detail', + `display` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'True if the detail can be displayed to the end user', + PRIMARY KEY (`id`), + CONSTRAINT `fk_extension_resource_map_details__map_id` FOREIGN KEY (`extension_resource_map_id`) + REFERENCES `extension_resource_map` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cloud`.`extension_custom_action` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `uuid` varchar(255) NOT NULL UNIQUE, + `name` varchar(255) NOT NULL, + `description` varchar(4096), + `extension_id` bigint(20) unsigned NOT NULL, + `resource_type` varchar(255), + `allowed_role_types` int unsigned NOT NULL DEFAULT '1', + `success_message` varchar(4096), + `error_message` varchar(4096), + `enabled` boolean DEFAULT true, + `timeout` int unsigned NOT NULL DEFAULT '5' COMMENT 'The timeout in seconds to wait for the action to complete before failing', + `created` datetime NOT NULL, + `removed` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + CONSTRAINT `fk_extension_custom_action__extension_id` FOREIGN KEY (`extension_id`) + REFERENCES `cloud`.`extension`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cloud`.`extension_custom_action_details` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `extension_custom_action_id` bigint(20) unsigned NOT NULL, + `name` varchar(255) NOT NULL, + `value` TEXT NOT NULL, + `display` tinyint(1) NOT NULL DEFAULT 1, + PRIMARY KEY (`id`), + CONSTRAINT `fk_custom_action_details__action_id` FOREIGN KEY (`extension_custom_action_id`) + REFERENCES `cloud`.`extension_custom_action`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vm_template', 'extension_id', 'bigint unsigned DEFAULT NULL COMMENT "id of the extension"'); + +-- Add built-in Extensions and Custom Actions + +DROP PROCEDURE IF EXISTS `cloud`.`INSERT_EXTENSION_IF_NOT_EXISTS`; +CREATE PROCEDURE `cloud`.`INSERT_EXTENSION_IF_NOT_EXISTS`( + IN ext_name VARCHAR(255), + IN ext_desc VARCHAR(255), + IN ext_path VARCHAR(255) +) +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM `cloud`.`extension` WHERE `name` = ext_name + ) THEN + INSERT INTO `cloud`.`extension` ( + `uuid`, `name`, `description`, `type`, + `relative_path`, `path_ready`, + `is_user_defined`, `state`, `created`, `removed` + ) + VALUES ( + UUID(), ext_name, ext_desc, 'Orchestrator', + ext_path, 1, 0, 'Enabled', NOW(), NULL + ) +; END IF +;END; + +DROP PROCEDURE IF EXISTS `cloud`.`INSERT_EXTENSION_DETAIL_IF_NOT_EXISTS`; +CREATE PROCEDURE `cloud`.`INSERT_EXTENSION_DETAIL_IF_NOT_EXISTS`( + IN ext_name VARCHAR(255), + IN detail_key VARCHAR(255), + IN detail_value TEXT, + IN display TINYINT(1) +) +BEGIN + DECLARE ext_id BIGINT +; SELECT `id` INTO ext_id FROM `cloud`.`extension` WHERE `name` = ext_name LIMIT 1 +; IF NOT EXISTS ( + SELECT 1 FROM `cloud`.`extension_details` + WHERE `extension_id` = ext_id AND `name` = detail_key + ) THEN + INSERT INTO `cloud`.`extension_details` ( + `extension_id`, `name`, `value`, `display` + ) + VALUES ( + ext_id, detail_key, detail_value, display + ) +; END IF +;END; + +CALL `cloud`.`INSERT_EXTENSION_IF_NOT_EXISTS`('Proxmox', 'Sample extension for Proxmox written in bash', 'Proxmox/proxmox.sh'); +CALL `cloud`.`INSERT_EXTENSION_DETAIL_IF_NOT_EXISTS`('Proxmox', 'orchestratorrequirespreparevm', 'true', 0); + +CALL `cloud`.`INSERT_EXTENSION_IF_NOT_EXISTS`('HyperV', 'Sample extension for HyperV written in python', 'HyperV/hyperv.py'); + +DROP PROCEDURE IF EXISTS `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`; +CREATE PROCEDURE `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`( + IN ext_name VARCHAR(255), + IN action_name VARCHAR(255), + IN action_desc VARCHAR(4096), + IN resource_type VARCHAR(255), + IN allowed_roles INT UNSIGNED, + IN success_msg VARCHAR(4096), + IN error_msg VARCHAR(4096), + IN timeout_seconds INT UNSIGNED +) +BEGIN + DECLARE ext_id BIGINT +; SELECT `id` INTO ext_id FROM `cloud`.`extension` WHERE `name` = ext_name LIMIT 1 +; IF NOT EXISTS ( + SELECT 1 FROM `cloud`.`extension_custom_action` WHERE `name` = action_name AND `extension_id` = ext_id + ) THEN + INSERT INTO `cloud`.`extension_custom_action` ( + `uuid`, `name`, `description`, `extension_id`, `resource_type`, + `allowed_role_types`, `success_message`, `error_message`, + `enabled`, `timeout`, `created`, `removed` + ) + VALUES ( + UUID(), action_name, action_desc, ext_id, resource_type, + allowed_roles, success_msg, error_msg, + 1, timeout_seconds, NOW(), NULL + ) +; END IF +;END; + +DROP PROCEDURE IF EXISTS `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`; +CREATE PROCEDURE `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS` ( + IN ext_name VARCHAR(255), + IN action_name VARCHAR(255), + IN param_json TEXT +) +BEGIN + DECLARE action_id BIGINT UNSIGNED +; SELECT `eca`.`id` INTO action_id FROM `cloud`.`extension_custom_action` `eca` + JOIN `cloud`.`extension` `e` ON `e`.`id` = `eca`.`extension_id` + WHERE `eca`.`name` = action_name AND `e`.`name` = ext_name LIMIT 1 +; IF NOT EXISTS ( + SELECT 1 FROM `cloud`.`extension_custom_action_details` + WHERE `extension_custom_action_id` = action_id + AND `name` = 'parameters' + ) THEN + INSERT INTO `cloud`.`extension_custom_action_details` ( + `extension_custom_action_id`, + `name`, + `value`, + `display` + ) VALUES ( + action_id, + 'parameters', + param_json, + 0 + ) +; END IF +;END; + +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('Proxmox', 'ListSnapshots', 'List Instance snapshots', 'VirtualMachine', 15, 'Snapshots fetched for {{resourceName}} in {{extensionName}}', 'List Snapshots failed for {{resourceName}}', 60); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('Proxmox', 'CreateSnapshot', 'Create an Instance snapshot', 'VirtualMachine', 15, 'Snapshot created for {{resourceName}} in {{extensionName}}', 'Snapshot creation failed for {{resourceName}}', 60); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('Proxmox', 'RestoreSnapshot', 'Restore Instance to the specific snapshot', 'VirtualMachine', 15, 'Successfully restored snapshot for {{resourceName}} in {{extensionName}}', 'Restore snapshot failed for {{resourceName}}', 60); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('Proxmox', 'DeleteSnapshot', 'Delete the specified snapshot', 'VirtualMachine', 15, 'Successfully deleted snapshot for {{resourceName}} in {{extensionName}}', 'Delete snapshot failed for {{resourceName}}', 60); + +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'Proxmox', + 'ListSnapshots', + '[]' +); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'Proxmox', + 'CreateSnapshot', + '[ + { + "name": "snap_name", + "type": "STRING", + "validationformat": "NONE", + "required": true + }, + { + "name": "snap_description", + "type": "STRING", + "validationformat": "NONE", + "required": false + }, + { + "name": "snap_save_memory", + "type": "BOOLEAN", + "validationformat": "NONE", + "required": false + } + ]' +); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'Proxmox', + 'RestoreSnapshot', + '[ + { + "name": "snap_name", + "type": "STRING", + "validationformat": "NONE", + "required": true + } + ]' +); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'Proxmox', + 'DeleteSnapshot', + '[ + { + "name": "snap_name", + "type": "STRING", + "validationformat": "NONE", + "required": true + } + ]' +); + +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('HyperV', 'ListSnapshots', 'List checkpoints/snapshots for the Instance', 'VirtualMachine', 15, 'Snapshots fetched for {{resourceName}} in {{extensionName}}', 'List Snapshots failed for {{resourceName}}', 60); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('HyperV', 'CreateSnapshot', 'Create a checkpoint/snapshot for the Instance', 'VirtualMachine', 15, 'Snapshot created for {{resourceName}} in {{extensionName}}', 'Snapshot creation failed for {{resourceName}}', 60); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('HyperV', 'RestoreSnapshot', 'Restore Instance to the specified snapshot', 'VirtualMachine', 15, 'Successfully restored snapshot for {{resourceName}} in {{extensionName}}', 'Restore snapshot failed for {{resourceName}}', 60); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('HyperV', 'DeleteSnapshot', 'Delete the specified snapshot', 'VirtualMachine', 15, 'Successfully deleted snapshot for {{resourceName}} in {{extensionName}}', 'Delete snapshot failed for {{resourceName}}', 60); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('HyperV', 'Suspend', 'Suspend the Instance by freezing its current state in RAM', 'VirtualMachine', 15, 'Successfully suspended {{resourceName}} in {{extensionName}}', 'Suspend failed for {{resourceName}}', 60); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('HyperV', 'Resume', 'Resumes a suspended Instance, restoring CPU execution from memory.', 'VirtualMachine', 15, 'Successfully resumed {{resourceName}} in {{extensionName}}', 'Resume failed for {{resourceName}}', 60); + +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'HyperV', + 'ListSnapshots', + '[]' +); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'HyperV', + 'CreateSnapshot', + '[ + { + "name": "snapshot_name", + "type": "STRING", + "validationformat": "NONE", + "required": true + } + ]' +); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'HyperV', + 'RestoreSnapshot', + '[ + { + "name": "snapshot_name", + "type": "STRING", + "validationformat": "NONE", + "required": true + } + ]' +); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'HyperV', + 'DeleteSnapshot', + '[ + { + "name": "snapshot_name", + "type": "STRING", + "validationformat": "NONE", + "required": true + } + ]' +); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'HyperV', + 'Suspend', + '[]' +); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'HyperV', + 'Resume', + '[]' +); + +ALTER TABLE `cloud`.`networks` MODIFY COLUMN `cidr` varchar(255) DEFAULT NULL COMMENT 'CloudStack managed vms get IP address from cidr.In general this cidr also serves as the network CIDR. But in case IP reservation feature is being used by a Guest network, networkcidr is the Effective network CIDR for that network'; +ALTER TABLE `cloud`.`networks` MODIFY COLUMN `gateway` varchar(255) DEFAULT NULL COMMENT 'gateway(s) for this network configuration'; +ALTER TABLE `cloud`.`networks` MODIFY COLUMN `ip6_cidr` varchar(1024) DEFAULT NULL COMMENT 'IPv6 cidr(s) for this network'; +ALTER TABLE `cloud`.`networks` MODIFY COLUMN `ip6_gateway` varchar(1024) DEFAULT NULL COMMENT 'IPv6 gateway(s) for this network'; + +-- Add columns name, description and backup_interval_type to backup table +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'name', 'VARCHAR(255) NULL COMMENT "name of the backup"'); +UPDATE `cloud`.`backups` backup INNER JOIN `cloud`.`vm_instance` vm ON backup.vm_id = vm.id SET backup.name = vm.name; +ALTER TABLE `cloud`.`backups` MODIFY COLUMN `name` VARCHAR(255) NOT NULL; +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'description', 'VARCHAR(1024) COMMENT "description for the backup"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'backup_interval_type', 'int(5) COMMENT "type of backup, e.g. manual, recurring - hourly, daily, weekly or monthly"'); + +-- Create backup details table +CREATE TABLE IF NOT EXISTS `cloud`.`backup_details` ( + `id` bigint unsigned NOT NULL auto_increment, + `backup_id` bigint unsigned NOT NULL COMMENT 'backup id', + `name` varchar(255) NOT NULL, + `value` TEXT NOT NULL, + `display` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Should detail be displayed to the end user', + PRIMARY KEY (`id`), + CONSTRAINT `fk_backup_details__backup_id` FOREIGN KEY `fk_backup_details__backup_id`(`backup_id`) REFERENCES `backups`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- Add diskOfferingId, deviceId, minIops and maxIops to backed_volumes in backups table +UPDATE `cloud`.`backups` b +INNER JOIN `cloud`.`vm_instance` vm ON b.vm_id = vm.id +SET b.backed_volumes = ( + SELECT CONCAT("[", + GROUP_CONCAT( + CONCAT( + "{\"uuid\":\"", v.uuid, "\",", + "\"type\":\"", v.volume_type, "\",", + "\"size\":", v.`size`, ",", + "\"path\":\"", IFNULL(v.path, 'null'), "\",", + "\"deviceId\":", IFNULL(v.device_id, 'null'), ",", + "\"diskOfferingId\":\"", doff.uuid, "\",", + "\"minIops\":", IFNULL(v.min_iops, 'null'), ",", + "\"maxIops\":", IFNULL(v.max_iops, 'null'), + "}" + ) + SEPARATOR "," + ), + "]") + FROM `cloud`.`volumes` v + LEFT JOIN `cloud`.`disk_offering` doff ON v.disk_offering_id = doff.id + WHERE v.instance_id = vm.id +); + +-- Add diskOfferingId, deviceId, minIops and maxIops to backup_volumes in vm_instance table +UPDATE `cloud`.`vm_instance` vm +SET vm.backup_volumes = ( + SELECT CONCAT("[", + GROUP_CONCAT( + CONCAT( + "{\"uuid\":\"", v.uuid, "\",", + "\"type\":\"", v.volume_type, "\",", + "\"size\":", v.`size`, ",", + "\"path\":\"", IFNULL(v.path, 'null'), "\",", + "\"deviceId\":", IFNULL(v.device_id, 'null'), ",", + "\"diskOfferingId\":\"", doff.uuid, "\",", + "\"minIops\":", IFNULL(v.min_iops, 'null'), ",", + "\"maxIops\":", IFNULL(v.max_iops, 'null'), + "}" + ) + SEPARATOR "," + ), + "]") + FROM `cloud`.`volumes` v + LEFT JOIN `cloud`.`disk_offering` doff ON v.disk_offering_id = doff.id + WHERE v.instance_id = vm.id +) +WHERE vm.backup_offering_id IS NOT NULL; + +-- Add column allocated_size to object_store table. Rename column 'used_bytes' to 'used_size' +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.object_store', 'allocated_size', 'bigint unsigned COMMENT "allocated size in bytes"'); +ALTER TABLE `cloud`.`object_store` CHANGE COLUMN `used_bytes` `used_size` BIGINT UNSIGNED COMMENT 'used size in bytes'; +ALTER TABLE `cloud`.`object_store` MODIFY COLUMN `total_size` bigint unsigned COMMENT 'total size in bytes'; +UPDATE `cloud`.`object_store` +JOIN ( + SELECT object_store_id, SUM(quota) AS total_quota + FROM `cloud`.`bucket` + WHERE removed IS NULL + GROUP BY object_store_id +) buckets_quota_sum_view ON `object_store`.id = buckets_quota_sum_view.object_store_id +SET `object_store`.allocated_size = buckets_quota_sum_view.total_quota; + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.console_session', 'domain_id', 'bigint(20) unsigned NOT NULL'); + +UPDATE `cloud`.`console_session` `cs` +SET `cs`.`domain_id` = ( + SELECT `acc`.`domain_id` + FROM `cloud`.`account` `acc` + WHERE `acc`.`id` = `cs`.`account_id` +); + +-- Re-apply VPC: update default network offering for vpc tier to conserve_mode=1 (#8309) +UPDATE `cloud`.`network_offerings` SET conserve_mode = 1 WHERE name = 'DefaultIsolatedNetworkOfferingForVpcNetworks'; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-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-420to421.sql b/engine/schema/src/main/resources/META-INF/db/schema-420to421.sql index b99af287bc5e..25c025c56511 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-420to421.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-420to421.sql @@ -20,10 +20,10 @@ --; -INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 's3.singleupload.max.size', '5', +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 's3.singleupload.max.size', '5', 'The maximum size limit for S3 single part upload API(in GB). If it is set to 0, then it means always use multi-part upload to upload object to S3. If it is set to -1, then it means always use single-part upload to upload object to S3.'); -INSERT IGNORE INTO `cloud`.`configuration` VALUES ("Storage", 'DEFAULT', 'management-server', "enable.ha.storage.migration", "true", "Enable/disable storage migration across primary storage during HA"); +INSERT IGNORE INTO `cloud`.`configuration` VALUES ("Storage", 'DEFAULT', 'management-server', "enable.ha.storage.migration", "true", "Enable/disable storage migration across primary storage during HA"); UPDATE `cloud`.`configuration` SET description="Specify whether or not to reserve CPU based on CPU overprovisioning factor" where name="vmware.reserve.cpu"; UPDATE `cloud`.`configuration` SET description="Specify whether or not to reserve memory based on memory overprovisioning factor" where name="vmware.reserve.mem"; -- Remove Windows Server 8 from guest_os_type dropdown to use Windows Server 2012 diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42100to42200-cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-42100to42200-cleanup.sql new file mode 100644 index 000000000000..0546613dfa34 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/schema-42100to42200-cleanup.sql @@ -0,0 +1,20 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +--; +-- Schema upgrade cleanup from 4.21.0.0 to 4.22.0.0 +--; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42100to42200.sql b/engine/schema/src/main/resources/META-INF/db/schema-42100to42200.sql new file mode 100644 index 000000000000..858c46a7c1ee --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/schema-42100to42200.sql @@ -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. + +--; +-- 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..54baf226ac43 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/schema-42200to42210-cleanup.sql @@ -0,0 +1,20 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +--; +-- Schema upgrade cleanup from 4.22.0.0 to 4.22.1.0 +--; 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..a8a3d3f7bd4f --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/schema-42200to42210.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. + +--; +-- 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'; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42210to42300-cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-42210to42300-cleanup.sql new file mode 100644 index 000000000000..e2b066af7800 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/schema-42210to42300-cleanup.sql @@ -0,0 +1,20 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +--; +-- Schema upgrade cleanup from 4.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..6d02bc314f7b --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql @@ -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. + +--; +-- Schema upgrade from 4.22.1.0 to 4.23.0.0 +--; + +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'; + +-- 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(100), + `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" API 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'); diff --git a/engine/schema/src/main/resources/META-INF/db/schema-442to450.sql b/engine/schema/src/main/resources/META-INF/db/schema-442to450.sql index 90a52bd42732..d2ba408241e0 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-442to450.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-442to450.sql @@ -671,7 +671,7 @@ CREATE VIEW `cloud`.`user_vm_view` AS resource_tags.resource_id tag_resource_id, resource_tags.resource_uuid tag_resource_uuid, resource_tags.resource_type tag_resource_type, - resource_tags.customer tag_customer, + resource_tags.customer tag_customer, async_job.id job_id, async_job.uuid job_uuid, async_job.job_status job_status, @@ -752,7 +752,7 @@ CREATE VIEW `cloud`.`user_vm_view` AS left join `cloud`.`user_vm_details` `custom_speed` ON (((`custom_speed`.`vm_id` = `cloud`.`vm_instance`.`id`) and (`custom_speed`.`name` = 'CpuSpeed'))) left join - `cloud`.`user_vm_details` `custom_ram_size` ON (((`custom_ram_size`.`vm_id` = `cloud`.`vm_instance`.`id`) and (`custom_ram_size`.`name` = 'memory'))); + `cloud`.`user_vm_details` `custom_ram_size` ON (((`custom_ram_size`.`vm_id` = `cloud`.`vm_instance`.`id`) and (`custom_ram_size`.`name` = 'memory'))); INSERT IGNORE INTO `cloud`.`guest_os` (id, uuid, category_id, display_name, created) VALUES (231, UUID(), 1, 'CentOS 5 (32-bit)', utc_timestamp()); diff --git a/engine/schema/src/main/resources/META-INF/db/schema-481to490-cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-481to490-cleanup.sql index 1868a0908006..b8dd0477db96 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-481to490-cleanup.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-481to490-cleanup.sql @@ -22,7 +22,7 @@ -- Added in CLOUDSTACK-9340: General DB optimization, 4 cases: ----- 1) Incorrect PRIMARY key -ALTER TABLE `cloud`.`ovs_tunnel_network` +ALTER TABLE `cloud`.`ovs_tunnel_network` DROP PRIMARY KEY, ADD PRIMARY KEY (`id`), DROP INDEX `id` , diff --git a/engine/schema/src/main/resources/META-INF/db/schema-481to490.sql b/engine/schema/src/main/resources/META-INF/db/schema-481to490.sql index 49cfc8346c54..bac3b1e6fabd 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-481to490.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-481to490.sql @@ -23,9 +23,9 @@ ALTER TABLE `event` ADD INDEX `archived` (`archived`); ALTER TABLE `event` ADD INDEX `state` (`state`); DROP VIEW IF EXISTS `cloud`.`template_view`; -CREATE +CREATE VIEW `template_view` AS - SELECT + SELECT `vm_template`.`id` AS `id`, `vm_template`.`uuid` AS `uuid`, `vm_template`.`unique_name` AS `unique_name`, @@ -124,9 +124,9 @@ VIEW `template_view` AS OR (`resource_tags`.`resource_type` = 'ISO'))))); DROP VIEW IF EXISTS `cloud`.`volume_view`; -CREATE +CREATE VIEW `volume_view` AS - SELECT + SELECT `volumes`.`id` AS `id`, `volumes`.`uuid` AS `uuid`, `volumes`.`name` AS `name`, @@ -234,9 +234,9 @@ VIEW `volume_view` AS AND (`async_job`.`job_status` = 0)))); DROP VIEW IF EXISTS `cloud`.`user_vm_view`; -CREATE +CREATE VIEW `user_vm_view` AS - SELECT + SELECT `vm_instance`.`id` AS `id`, `vm_instance`.`name` AS `name`, `user_vm`.`display_name` AS `display_name`, @@ -423,10 +423,10 @@ ALTER TABLE `cloud`.`ssh_keypairs` ADD INDEX `i_public_key` (`public_key` (64) A ALTER TABLE `cloud`.`user_vm_details` ADD INDEX `i_name_vm_id` (`vm_id` ASC, `name` ASC); ALTER TABLE `cloud`.`instance_group` ADD INDEX `i_name` (`name` ASC); ------ 4) Some views query (Change view to improve account retrieval speed) +----- 4) Some views query (Change view to improve account retrieval speed) CREATE OR REPLACE VIEW `account_vmstats_view` AS - SELECT + SELECT `vm_instance`.`account_id` AS `account_id`, `vm_instance`.`state` AS `state`, COUNT(0) AS `vmcount` diff --git a/engine/schema/src/main/resources/META-INF/db/schema-4910to4920.sql b/engine/schema/src/main/resources/META-INF/db/schema-4910to4920.sql index a910a8b77998..1aa630201241 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-4910to4920.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-4910to4920.sql @@ -19,42 +19,6 @@ -- Schema upgrade from 4.9.1.0 to 4.9.2.0; --; ---; --- Stored procedure to do idempotent insert; ---; - -DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_INSERT_GUESTOS_HYPERVISOR_MAPPING`; - -CREATE PROCEDURE `cloud`.`IDEMPOTENT_INSERT_GUESTOS_HYPERVISOR_MAPPING`( - IN in_hypervisor_type VARCHAR(32), - IN in_hypervisor_version VARCHAR(32), - IN in_guest_os_name VARCHAR(255), - IN in_guest_os_id BIGINT(20) UNSIGNED, - IN is_user_defined int(1) UNSIGNED) -BEGIN - IF NOT EXISTS ((SELECT * FROM `cloud`.`guest_os_hypervisor` WHERE - hypervisor_type=in_hypervisor_type AND - hypervisor_version=in_hypervisor_version AND - guest_os_id = in_guest_os_id)) - THEN - INSERT INTO `cloud`.`guest_os_hypervisor` ( - uuid, - hypervisor_type, - hypervisor_version, - guest_os_name, - guest_os_id, - created, - is_user_defined) - VALUES ( - UUID(), - in_hypervisor_type, - in_hypervisor_version, - in_guest_os_name, - in_guest_os_id, - utc_timestamp(), - is_user_defined - ); END IF; END;; - CALL `cloud`.`IDEMPOTENT_INSERT_GUESTOS_HYPERVISOR_MAPPING`('Xenserver', '7.0.0', 'CentOS 4.5 (32-bit)', 1, 0); CALL `cloud`.`IDEMPOTENT_INSERT_GUESTOS_HYPERVISOR_MAPPING`('Xenserver', '7.0.0', 'CentOS 4.6 (32-bit)', 2, 0); CALL `cloud`.`IDEMPOTENT_INSERT_GUESTOS_HYPERVISOR_MAPPING`('Xenserver', '7.0.0', 'CentOS 4.7 (32-bit)', 3, 0); @@ -234,5 +198,3 @@ CALL `cloud`.`IDEMPOTENT_INSERT_GUESTOS_HYPERVISOR_MAPPING`('Xenserver', '7.0.0' CALL `cloud`.`IDEMPOTENT_INSERT_GUESTOS_HYPERVISOR_MAPPING`('Xenserver', '7.0.0', 'Ubuntu Trusty Tahr 14.04', 255, 0); CALL `cloud`.`IDEMPOTENT_INSERT_GUESTOS_HYPERVISOR_MAPPING`('Xenserver', '7.0.0', 'Ubuntu Trusty Tahr 14.04', 256, 0); - -DROP PROCEDURE `cloud`.`IDEMPOTENT_INSERT_GUESTOS_HYPERVISOR_MAPPING` diff --git a/engine/schema/src/main/resources/META-INF/db/schema-4930to41000.sql b/engine/schema/src/main/resources/META-INF/db/schema-4930to41000.sql index dc0cd6d4d75a..23670757247a 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-4930to41000.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-4930to41000.sql @@ -147,7 +147,7 @@ CREATE TABLE IF NOT EXISTS `cloud`.`storage_pool_tags` ( ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; -- Insert storage tags from storage_pool_details -INSERT INTO `cloud`.`storage_pool_tags` (pool_id, tag) SELECT pool_id, +INSERT INTO `cloud`.`storage_pool_tags` (pool_id, tag) SELECT pool_id, name FROM `cloud`.`storage_pool_details` WHERE value = 'true'; -- Alter view storage_pool_view @@ -227,7 +227,7 @@ ALTER TABLE `cloud`.`vm_snapshots` ADD CONSTRAINT `fk_vm_snapshots_service_offer INSERT INTO `cloud`.`vm_snapshot_details` (vm_snapshot_id, name, value) SELECT s.id, d.name, d.value FROM `cloud`.`user_vm_details` d JOIN `cloud`.`vm_instance` v ON (d.vm_id = v.id) -JOIN `cloud`.`service_offering` o ON (v.service_offering_id = o.id) +JOIN `cloud`.`service_offering` o ON (v.service_offering_id = o.id) JOIN `cloud`.`vm_snapshots` s ON (s.service_offering_id = o.id AND s.vm_id = v.id) WHERE (o.cpu is null AND o.speed IS NULL AND o.ram_size IS NULL) AND (d.name = 'cpuNumber' OR d.name = 'cpuSpeed' OR d.name = 'memory'); diff --git a/engine/schema/src/main/resources/META-INF/db/schema-level.sql b/engine/schema/src/main/resources/META-INF/db/schema-level.sql index 72aade4e5016..fef961502fa5 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-level.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-level.sql @@ -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/engine/schema/src/main/resources/META-INF/db/schema-snapshot-217to224.sql b/engine/schema/src/main/resources/META-INF/db/schema-snapshot-217to224.sql index 7320bda59063..5e29435855df 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-snapshot-217to224.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-snapshot-217to224.sql @@ -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/engine/schema/src/main/resources/META-INF/db/schema-snapshot-223to224.sql b/engine/schema/src/main/resources/META-INF/db/schema-snapshot-223to224.sql index 668cbb692b99..5c27eed68f00 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-snapshot-223to224.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-snapshot-223to224.sql @@ -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/engine/schema/src/main/resources/META-INF/db/views/cloud.account_netstats_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.account_netstats_view.sql new file mode 100644 index 000000000000..11193c465fd7 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.account_netstats_view.sql @@ -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. + +-- cloud.account_netstats_view source + + +DROP VIEW IF EXISTS `cloud`.`account_netstats_view`; + +CREATE VIEW `cloud`.`account_netstats_view` AS +select + `user_statistics`.`account_id` AS `account_id`, + (sum(`user_statistics`.`net_bytes_received`) + sum(`user_statistics`.`current_bytes_received`)) AS `bytesReceived`, + (sum(`user_statistics`.`net_bytes_sent`) + sum(`user_statistics`.`current_bytes_sent`)) AS `bytesSent` +from + `user_statistics` +group by + `user_statistics`.`account_id`; diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.account_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.account_view.sql new file mode 100644 index 000000000000..edc164c40cbd --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.account_view.sql @@ -0,0 +1,205 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT 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`.`account_view`; + +DROP VIEW IF EXISTS `cloud`.`account_view`; +CREATE VIEW `cloud`.`account_view` AS +select + `account`.`id` AS `id`, + `account`.`uuid` AS `uuid`, + `account`.`account_name` AS `account_name`, + `account`.`type` AS `type`, + `account`.`role_id` AS `role_id`, + `account`.`state` AS `state`, + `account`.`created` AS `created`, + `account`.`removed` AS `removed`, + `account`.`cleanup_needed` AS `cleanup_needed`, + `account`.`network_domain` AS `network_domain` , + `account`.`default` AS `default`, + `account`.`api_key_access` AS `api_key_access`, + `domain`.`id` AS `domain_id`, + `domain`.`uuid` AS `domain_uuid`, + `domain`.`name` AS `domain_name`, + `domain`.`path` AS `domain_path`, + `data_center`.`id` AS `data_center_id`, + `data_center`.`uuid` AS `data_center_uuid`, + `data_center`.`name` AS `data_center_name`, + `account_netstats_view`.`bytesReceived` AS `bytesReceived`, + `account_netstats_view`.`bytesSent` AS `bytesSent`, + `vmlimit`.`max` AS `vmLimit`, + `vmcount`.`count` AS `vmTotal`, + `runningvm`.`vmcount` AS `runningVms`, + `stoppedvm`.`vmcount` AS `stoppedVms`, + `iplimit`.`max` AS `ipLimit`, + `ipcount`.`count` AS `ipTotal`, + `free_ip_view`.`free_ip` AS `ipFree`, + `volumelimit`.`max` AS `volumeLimit`, + `volumecount`.`count` AS `volumeTotal`, + `snapshotlimit`.`max` AS `snapshotLimit`, + `snapshotcount`.`count` AS `snapshotTotal`, + `templatelimit`.`max` AS `templateLimit`, + `templatecount`.`count` AS `templateTotal`, + `vpclimit`.`max` AS `vpcLimit`, + `vpccount`.`count` AS `vpcTotal`, + `projectlimit`.`max` AS `projectLimit`, + `projectcount`.`count` AS `projectTotal`, + `networklimit`.`max` AS `networkLimit`, + `networkcount`.`count` AS `networkTotal`, + `cpulimit`.`max` AS `cpuLimit`, + `cpucount`.`count` AS `cpuTotal`, + `memorylimit`.`max` AS `memoryLimit`, + `memorycount`.`count` AS `memoryTotal`, + `gpulimit`.`max` AS `gpuLimit`, + `gpucount`.`count` AS `gpuTotal`, + `primary_storage_limit`.`max` AS `primaryStorageLimit`, + `primary_storage_count`.`count` AS `primaryStorageTotal`, + `secondary_storage_limit`.`max` AS `secondaryStorageLimit`, + `secondary_storage_count`.`count` AS `secondaryStorageTotal`, + `backup_limit`.`max` AS `backupLimit`, + `backup_count`.`count` AS `backupTotal`, + `backup_storage_limit`.`max` AS `backupStorageLimit`, + `backup_storage_count`.`count` AS `backupStorageTotal`, + `bucket_limit`.`max` AS `bucketLimit`, + `bucket_count`.`count` AS `bucketTotal`, + `object_storage_limit`.`max` AS `objectStorageLimit`, + `object_storage_count`.`count` AS `objectStorageTotal`, + `async_job`.`id` AS `job_id`, + `async_job`.`uuid` AS `job_uuid`, + `async_job`.`job_status` AS `job_status`, + `async_job`.`account_id` AS `job_account_id` +from + `cloud`.`free_ip_view`, + `cloud`.`account` + inner join + `cloud`.`domain` ON account.domain_id = domain.id + left join + `cloud`.`data_center` ON account.default_zone_id = data_center.id + left join + `cloud`.`account_netstats_view` ON account.id = account_netstats_view.account_id + left join + `cloud`.`resource_limit` vmlimit ON account.id = vmlimit.account_id + and vmlimit.type = 'user_vm' and vmlimit.tag IS NULL + left join + `cloud`.`resource_count` vmcount ON account.id = vmcount.account_id + and vmcount.type = 'user_vm' and vmcount.tag IS NULL + left join + `cloud`.`account_vmstats_view` runningvm ON account.id = runningvm.account_id + and runningvm.state = 'Running' + left join + `cloud`.`account_vmstats_view` stoppedvm ON account.id = stoppedvm.account_id + and stoppedvm.state = 'Stopped' + left join + `cloud`.`resource_limit` iplimit ON account.id = iplimit.account_id + and iplimit.type = 'public_ip' + left join + `cloud`.`resource_count` ipcount ON account.id = ipcount.account_id + and ipcount.type = 'public_ip' + left join + `cloud`.`resource_limit` volumelimit ON account.id = volumelimit.account_id + and volumelimit.type = 'volume' and volumelimit.tag IS NULL + left join + `cloud`.`resource_count` volumecount ON account.id = volumecount.account_id + and volumecount.type = 'volume' and volumecount.tag IS NULL + left join + `cloud`.`resource_limit` snapshotlimit ON account.id = snapshotlimit.account_id + and snapshotlimit.type = 'snapshot' + left join + `cloud`.`resource_count` snapshotcount ON account.id = snapshotcount.account_id + and snapshotcount.type = 'snapshot' + left join + `cloud`.`resource_limit` templatelimit ON account.id = templatelimit.account_id + and templatelimit.type = 'template' + left join + `cloud`.`resource_count` templatecount ON account.id = templatecount.account_id + and templatecount.type = 'template' + left join + `cloud`.`resource_limit` vpclimit ON account.id = vpclimit.account_id + and vpclimit.type = 'vpc' + left join + `cloud`.`resource_count` vpccount ON account.id = vpccount.account_id + and vpccount.type = 'vpc' + left join + `cloud`.`resource_limit` projectlimit ON account.id = projectlimit.account_id + and projectlimit.type = 'project' + left join + `cloud`.`resource_count` projectcount ON account.id = projectcount.account_id + and projectcount.type = 'project' + left join + `cloud`.`resource_limit` networklimit ON account.id = networklimit.account_id + and networklimit.type = 'network' + left join + `cloud`.`resource_count` networkcount ON account.id = networkcount.account_id + and networkcount.type = 'network' + left join + `cloud`.`resource_limit` cpulimit ON account.id = cpulimit.account_id + and cpulimit.type = 'cpu' and cpulimit.tag IS NULL + left join + `cloud`.`resource_count` cpucount ON account.id = cpucount.account_id + and cpucount.type = 'cpu' and cpucount.tag IS NULL + left join + `cloud`.`resource_limit` memorylimit ON account.id = memorylimit.account_id + and memorylimit.type = 'memory' and memorylimit.tag IS NULL + left join + `cloud`.`resource_count` memorycount ON account.id = memorycount.account_id + and memorycount.type = 'memory' and memorycount.tag IS NULL + left join + `cloud`.`resource_limit` gpulimit ON account.id = gpulimit.account_id + and gpulimit.type = 'gpu' and gpulimit.tag IS NULL + left join + `cloud`.`resource_count` gpucount ON account.id = gpucount.account_id + and gpucount.type = 'gpu' and gpucount.tag IS NULL + left join + `cloud`.`resource_limit` primary_storage_limit ON account.id = primary_storage_limit.account_id + and primary_storage_limit.type = 'primary_storage' and primary_storage_limit.tag IS NULL + left join + `cloud`.`resource_count` primary_storage_count ON account.id = primary_storage_count.account_id + and primary_storage_count.type = 'primary_storage' and primary_storage_count.tag IS NULL + left join + `cloud`.`resource_limit` secondary_storage_limit ON account.id = secondary_storage_limit.account_id + and secondary_storage_limit.type = 'secondary_storage' + left join + `cloud`.`resource_count` secondary_storage_count ON account.id = secondary_storage_count.account_id + and secondary_storage_count.type = 'secondary_storage' + left join + `cloud`.`resource_limit` backup_limit ON account.id = backup_limit.account_id + and backup_limit.type = 'backup' + left join + `cloud`.`resource_count` backup_count ON account.id = backup_count.account_id + and backup_count.type = 'backup' + left join + `cloud`.`resource_limit` backup_storage_limit ON account.id = backup_storage_limit.account_id + and backup_storage_limit.type = 'backup_storage' + left join + `cloud`.`resource_count` backup_storage_count ON account.id = backup_storage_count.account_id + and backup_storage_count.type = 'backup_storage' + left join + `cloud`.`resource_limit` bucket_limit ON account.id = bucket_limit.account_id + and bucket_limit.type = 'bucket' + left join + `cloud`.`resource_count` bucket_count ON account.id = bucket_count.account_id + and bucket_count.type = 'bucket' + left join + `cloud`.`resource_limit` object_storage_limit ON account.id = object_storage_limit.account_id + and object_storage_limit.type = 'object_storage' + left join + `cloud`.`resource_count` object_storage_count ON account.id = object_storage_count.account_id + and object_storage_count.type = 'object_storage' + left join + `cloud`.`async_job` ON async_job.instance_id = account.id + and async_job.instance_type = 'Account' + and async_job.job_status = 0; diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.account_vmstats_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.account_vmstats_view.sql new file mode 100644 index 000000000000..df6a216b0f8e --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.account_vmstats_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. + +-- cloud.account_vmstats_view source + + +DROP VIEW IF EXISTS `cloud`.`account_vmstats_view`; + +CREATE VIEW `cloud`.`account_vmstats_view` AS +select + `vm_instance`.`account_id` AS `account_id`, + `vm_instance`.`state` AS `state`, + count(0) AS `vmcount` +from + `vm_instance` +where + ((`vm_instance`.`vm_type` = 'User') + and (`vm_instance`.`removed` is null)) +group by + `vm_instance`.`account_id`, + `vm_instance`.`state`; diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.affinity_group_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.affinity_group_view.sql new file mode 100644 index 000000000000..90a398e1ec5c --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.affinity_group_view.sql @@ -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. + +-- cloud.affinity_group_view source + + +DROP VIEW IF EXISTS `cloud`.`affinity_group_view`; + +CREATE VIEW `cloud`.`affinity_group_view` AS +select + `affinity_group`.`id` AS `id`, + `affinity_group`.`name` AS `name`, + `affinity_group`.`type` AS `type`, + `affinity_group`.`description` AS `description`, + `affinity_group`.`uuid` AS `uuid`, + `affinity_group`.`acl_type` AS `acl_type`, + `account`.`id` AS `account_id`, + `account`.`uuid` AS `account_uuid`, + `account`.`account_name` AS `account_name`, + `account`.`type` AS `account_type`, + `domain`.`id` AS `domain_id`, + `domain`.`uuid` AS `domain_uuid`, + `domain`.`name` AS `domain_name`, + `domain`.`path` AS `domain_path`, + `projects`.`id` AS `project_id`, + `projects`.`uuid` AS `project_uuid`, + `projects`.`name` AS `project_name`, + `vm_instance`.`id` AS `vm_id`, + `vm_instance`.`uuid` AS `vm_uuid`, + `vm_instance`.`name` AS `vm_name`, + `vm_instance`.`state` AS `vm_state`, + `user_vm`.`display_name` AS `vm_display_name` +from + ((((((`affinity_group` +join `account` on + ((`affinity_group`.`account_id` = `account`.`id`))) +join `domain` on + ((`affinity_group`.`domain_id` = `domain`.`id`))) +left join `projects` on + ((`projects`.`project_account_id` = `account`.`id`))) +left join `affinity_group_vm_map` on + ((`affinity_group`.`id` = `affinity_group_vm_map`.`affinity_group_id`))) +left join `vm_instance` on + ((`vm_instance`.`id` = `affinity_group_vm_map`.`instance_id`))) +left join `user_vm` on + ((`user_vm`.`id` = `vm_instance`.`id`))); diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.data_center_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.data_center_view.sql index c34df4f1cbf5..46aea863fc5e 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.data_center_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.data_center_view.sql @@ -42,6 +42,7 @@ select data_center.type, data_center.removed, data_center.sort_key, + data_center.storage_access_groups, domain.id domain_id, domain.uuid domain_uuid, domain.name domain_name, diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.disk_offering_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.disk_offering_view.sql index 10dd3c2f9ded..dffaec575ceb 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.disk_offering_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.disk_offering_view.sql @@ -76,7 +76,5 @@ FROM LEFT JOIN `cloud`.`disk_offering_details` AS `vsphere_storage_policy` ON `vsphere_storage_policy`.`offering_id` = `disk_offering`.`id` AND `vsphere_storage_policy`.`name` = 'storagepolicy' -WHERE - `disk_offering`.`state`='Active' GROUP BY `disk_offering`.`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 70394e8fd6d3..a12e02bcfdb8 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_router_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_router_view.sql @@ -58,6 +58,7 @@ select host.resource_state host_resource_state, vm_template.id template_id, vm_template.uuid template_uuid, + vm_template.arch arch, service_offering.id service_offering_id, service_offering.uuid service_offering_uuid, service_offering.name service_offering_name, diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_view.sql new file mode 100644 index 000000000000..14fd87536aa1 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_view.sql @@ -0,0 +1,175 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT 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`.`domain_view`; + +DROP VIEW IF EXISTS `cloud`.`domain_view`; + +CREATE VIEW `cloud`.`domain_view` AS +select + `domain`.`id` AS `id`, + `domain`.`parent` AS `parent`, + `domain`.`name` AS `name`, + `domain`.`uuid` AS `uuid`, + `domain`.`owner` AS `owner`, + `domain`.`path` AS `path`, + `domain`.`level` AS `level`, + `domain`.`child_count` AS `child_count`, + `domain`.`next_child_seq` AS `next_child_seq`, + `domain`.`created` AS `created`, + `domain`.`removed` AS `removed`, + `domain`.`state` AS `state`, + `domain`.`network_domain` AS `network_domain`, + `domain`.`type` AS `type`, + `vmlimit`.`max` AS `vmLimit`, + `vmcount`.`count` AS `vmTotal`, + `iplimit`.`max` AS `ipLimit`, + `ipcount`.`count` AS `ipTotal`, + `volumelimit`.`max` AS `volumeLimit`, + `volumecount`.`count` AS `volumeTotal`, + `snapshotlimit`.`max` AS `snapshotLimit`, + `snapshotcount`.`count` AS `snapshotTotal`, + `templatelimit`.`max` AS `templateLimit`, + `templatecount`.`count` AS `templateTotal`, + `vpclimit`.`max` AS `vpcLimit`, + `vpccount`.`count` AS `vpcTotal`, + `projectlimit`.`max` AS `projectLimit`, + `projectcount`.`count` AS `projectTotal`, + `networklimit`.`max` AS `networkLimit`, + `networkcount`.`count` AS `networkTotal`, + `cpulimit`.`max` AS `cpuLimit`, + `cpucount`.`count` AS `cpuTotal`, + `memorylimit`.`max` AS `memoryLimit`, + `memorycount`.`count` AS `memoryTotal`, + `gpulimit`.`max` AS `gpuLimit`, + `gpucount`.`count` AS `gpuTotal`, + `primary_storage_limit`.`max` AS `primaryStorageLimit`, + `primary_storage_count`.`count` AS `primaryStorageTotal`, + `secondary_storage_limit`.`max` AS `secondaryStorageLimit`, + `secondary_storage_count`.`count` AS `secondaryStorageTotal`, + `backup_limit`.`max` AS `backupLimit`, + `backup_count`.`count` AS `backupTotal`, + `backup_storage_limit`.`max` AS `backupStorageLimit`, + `backup_storage_count`.`count` AS `backupStorageTotal`, + `bucket_limit`.`max` AS `bucketLimit`, + `bucket_count`.`count` AS `bucketTotal`, + `object_storage_limit`.`max` AS `objectStorageLimit`, + `object_storage_count`.`count` AS `objectStorageTotal` +from + `cloud`.`domain` + left join + `cloud`.`resource_limit` vmlimit ON domain.id = vmlimit.domain_id + and vmlimit.type = 'user_vm' and vmlimit.tag IS NULL + left join + `cloud`.`resource_count` vmcount ON domain.id = vmcount.domain_id + and vmcount.type = 'user_vm' and vmcount.tag IS NULL + left join + `cloud`.`resource_limit` iplimit ON domain.id = iplimit.domain_id + and iplimit.type = 'public_ip' + left join + `cloud`.`resource_count` ipcount ON domain.id = ipcount.domain_id + and ipcount.type = 'public_ip' + left join + `cloud`.`resource_limit` volumelimit ON domain.id = volumelimit.domain_id + and volumelimit.type = 'volume' and volumelimit.tag IS NULL + left join + `cloud`.`resource_count` volumecount ON domain.id = volumecount.domain_id + and volumecount.type = 'volume' and volumecount.tag IS NULL + left join + `cloud`.`resource_limit` snapshotlimit ON domain.id = snapshotlimit.domain_id + and snapshotlimit.type = 'snapshot' + left join + `cloud`.`resource_count` snapshotcount ON domain.id = snapshotcount.domain_id + and snapshotcount.type = 'snapshot' + left join + `cloud`.`resource_limit` templatelimit ON domain.id = templatelimit.domain_id + and templatelimit.type = 'template' + left join + `cloud`.`resource_count` templatecount ON domain.id = templatecount.domain_id + and templatecount.type = 'template' + left join + `cloud`.`resource_limit` vpclimit ON domain.id = vpclimit.domain_id + and vpclimit.type = 'vpc' + left join + `cloud`.`resource_count` vpccount ON domain.id = vpccount.domain_id + and vpccount.type = 'vpc' + left join + `cloud`.`resource_limit` projectlimit ON domain.id = projectlimit.domain_id + and projectlimit.type = 'project' + left join + `cloud`.`resource_count` projectcount ON domain.id = projectcount.domain_id + and projectcount.type = 'project' + left join + `cloud`.`resource_limit` networklimit ON domain.id = networklimit.domain_id + and networklimit.type = 'network' + left join + `cloud`.`resource_count` networkcount ON domain.id = networkcount.domain_id + and networkcount.type = 'network' + left join + `cloud`.`resource_limit` cpulimit ON domain.id = cpulimit.domain_id + and cpulimit.type = 'cpu' and cpulimit.tag IS NULL + left join + `cloud`.`resource_count` cpucount ON domain.id = cpucount.domain_id + and cpucount.type = 'cpu' and cpucount.tag IS NULL + left join + `cloud`.`resource_limit` memorylimit ON domain.id = memorylimit.domain_id + and memorylimit.type = 'memory' and memorylimit.tag IS NULL + left join + `cloud`.`resource_count` memorycount ON domain.id = memorycount.domain_id + and memorycount.type = 'memory' and memorycount.tag IS NULL + left join + `cloud`.`resource_limit` gpulimit ON domain.id = gpulimit.domain_id + and gpulimit.type = 'gpu' and gpulimit.tag IS NULL + left join + `cloud`.`resource_count` gpucount ON domain.id = gpucount.domain_id + and gpucount.type = 'gpu' and gpucount.tag IS NULL + left join + `cloud`.`resource_limit` primary_storage_limit ON domain.id = primary_storage_limit.domain_id + and primary_storage_limit.type = 'primary_storage' and primary_storage_limit.tag IS NULL + left join + `cloud`.`resource_count` primary_storage_count ON domain.id = primary_storage_count.domain_id + and primary_storage_count.type = 'primary_storage' and primary_storage_count.tag IS NULL + left join + `cloud`.`resource_limit` secondary_storage_limit ON domain.id = secondary_storage_limit.domain_id + and secondary_storage_limit.type = 'secondary_storage' + left join + `cloud`.`resource_count` secondary_storage_count ON domain.id = secondary_storage_count.domain_id + and secondary_storage_count.type = 'secondary_storage' + left join + `cloud`.`resource_limit` backup_limit ON domain.id = backup_limit.domain_id + and backup_limit.type = 'backup' + left join + `cloud`.`resource_count` backup_count ON domain.id = backup_count.domain_id + and backup_count.type = 'backup' + left join + `cloud`.`resource_limit` backup_storage_limit ON domain.id = backup_storage_limit.domain_id + and backup_storage_limit.type = 'backup_storage' + left join + `cloud`.`resource_count` backup_storage_count ON domain.id = backup_storage_count.domain_id + and backup_storage_count.type = 'backup_storage' + left join + `cloud`.`resource_limit` bucket_limit ON domain.id = bucket_limit.domain_id + and bucket_limit.type = 'bucket' + left join + `cloud`.`resource_count` bucket_count ON domain.id = bucket_count.domain_id + and bucket_count.type = 'bucket' + left join + `cloud`.`resource_limit` object_storage_limit ON domain.id = object_storage_limit.domain_id + and object_storage_limit.type = 'object_storage' + left join + `cloud`.`resource_count` object_storage_count ON domain.id = object_storage_count.domain_id + and object_storage_count.type = 'object_storage'; diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.event_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.event_view.sql new file mode 100644 index 000000000000..0a15ae4c0c91 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.event_view.sql @@ -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. + +-- cloud.event_view source + + +DROP VIEW IF EXISTS `cloud`.`event_view`; + +CREATE VIEW `cloud`.`event_view` AS +select + `event`.`id` AS `id`, + `event`.`uuid` AS `uuid`, + `event`.`type` AS `type`, + `event`.`state` AS `state`, + `event`.`description` AS `description`, + `event`.`resource_id` AS `resource_id`, + `event`.`resource_type` AS `resource_type`, + `event`.`created` AS `created`, + `event`.`level` AS `level`, + `event`.`parameters` AS `parameters`, + `event`.`start_id` AS `start_id`, + `eve`.`uuid` AS `start_uuid`, + `event`.`user_id` AS `user_id`, + `event`.`archived` AS `archived`, + `event`.`display` AS `display`, + `user`.`username` AS `user_name`, + `account`.`id` AS `account_id`, + `account`.`uuid` AS `account_uuid`, + `account`.`account_name` AS `account_name`, + `account`.`type` AS `account_type`, + `domain`.`id` AS `domain_id`, + `domain`.`uuid` AS `domain_uuid`, + `domain`.`name` AS `domain_name`, + `domain`.`path` AS `domain_path`, + `projects`.`id` AS `project_id`, + `projects`.`uuid` AS `project_uuid`, + `projects`.`name` AS `project_name` +from + (((((`event` +join `account` on + ((`event`.`account_id` = `account`.`id`))) +join `domain` on + ((`event`.`domain_id` = `domain`.`id`))) +join `user` on + ((`event`.`user_id` = `user`.`id`))) +left join `projects` on + ((`projects`.`project_account_id` = `event`.`account_id`))) +left join `event` `eve` on + ((`event`.`start_id` = `eve`.`id`))); diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.free_ip_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.free_ip_view.sql new file mode 100644 index 000000000000..29c22f332154 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.free_ip_view.sql @@ -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. + +-- cloud.free_ip_view source + + +DROP VIEW IF EXISTS `cloud`.`free_ip_view`; + +CREATE VIEW `cloud`.`free_ip_view` AS +select + count(`user_ip_address`.`id`) AS `free_ip` +from + (`user_ip_address` +join `vlan` on + (((`vlan`.`id` = `user_ip_address`.`vlan_db_id`) + and (`vlan`.`vlan_type` = 'VirtualNetwork')))) +where + (`user_ip_address`.`state` = 'Free'); diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.gui_themes_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.gui_themes_view.sql new file mode 100644 index 000000000000..3173274623ed --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.gui_themes_view.sql @@ -0,0 +1,38 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +-- VIEW `cloud`.`gui_themes_view`; + +DROP VIEW IF EXISTS `cloud`.`gui_themes_view`; + +CREATE VIEW `cloud`.`gui_themes_view` AS +SELECT + `cloud`.`gui_themes`.`id` AS `id`, + `cloud`.`gui_themes`.`uuid` AS `uuid`, + `cloud`.`gui_themes`.`name` AS `name`, + `cloud`.`gui_themes`.`description` AS `description`, + `cloud`.`gui_themes`.`css` AS `css`, + `cloud`.`gui_themes`.`json_configuration` AS `json_configuration`, + (SELECT group_concat(gtd.`value` separator ',') FROM `cloud`.`gui_themes_details` gtd WHERE gtd.`type` = 'commonName' AND gtd.gui_theme_id = `cloud`.`gui_themes`.`id`) common_names, + (SELECT group_concat(gtd.`value` separator ',') FROM `cloud`.`gui_themes_details` gtd WHERE gtd.`type` = 'domain' AND gtd.gui_theme_id = `cloud`.`gui_themes`.`id`) domains, + (SELECT group_concat(gtd.`value` separator ',') FROM `cloud`.`gui_themes_details` gtd WHERE gtd.`type` = 'account' AND gtd.gui_theme_id = `cloud`.`gui_themes`.`id`) accounts, + `cloud`.`gui_themes`.`recursive_domains` AS `recursive_domains`, + `cloud`.`gui_themes`.`is_public` AS `is_public`, + `cloud`.`gui_themes`.`created` AS `created`, + `cloud`.`gui_themes`.`removed` AS `removed` +FROM `cloud`.`gui_themes` LEFT JOIN `cloud`.`gui_themes_details` ON `cloud`.`gui_themes_details`.`gui_theme_id` = `cloud`.`gui_themes`.`id` +GROUP BY `cloud`.`gui_themes`.`id`; diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.host_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.host_view.sql index 5c6d4fd772b3..d9f4e2671595 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 @@ -41,19 +41,26 @@ SELECT host.cpus, host.speed, host.ram, + host.arch, + host.storage_access_groups, cluster.id cluster_id, cluster.uuid cluster_uuid, cluster.name cluster_name, cluster.cluster_type, + cluster.storage_access_groups AS cluster_storage_access_groups, data_center.id data_center_id, data_center.uuid data_center_uuid, data_center.name data_center_name, + data_center.storage_access_groups AS zone_storage_access_groups, data_center.networktype data_center_type, host_pod_ref.id pod_id, host_pod_ref.uuid pod_uuid, host_pod_ref.name pod_name, + host_pod_ref.storage_access_groups AS pod_storage_access_groups, GROUP_CONCAT(DISTINCT(host_tags.tag)) AS tag, - `host_tags`.`is_tag_a_rule` AS `is_tag_a_rule`, + GROUP_CONCAT(DISTINCT(explicit_host_tags.tag)) AS explicit_tag, + GROUP_CONCAT(DISTINCT(implicit_host_tags.tag)) AS implicit_tag, + `explicit_host_tags`.`is_tag_a_rule` AS `is_tag_a_rule`, guest_os_category.id guest_os_category_id, guest_os_category.uuid guest_os_category_uuid, guest_os_category.name guest_os_category_name, @@ -89,6 +96,10 @@ FROM LEFT JOIN `cloud`.`host_tags` ON host_tags.host_id = host.id LEFT JOIN + `cloud`.`host_tags` AS explicit_host_tags ON explicit_host_tags.host_id = host.id AND explicit_host_tags.is_implicit = 0 + LEFT JOIN + `cloud`.`host_tags` AS implicit_host_tags ON implicit_host_tags.host_id = host.id AND implicit_host_tags.is_implicit = 1 + LEFT JOIN `cloud`.`op_host_capacity` mem_caps ON host.id = mem_caps.host_id AND mem_caps.capacity_type = 0 LEFT JOIN diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.image_store_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.image_store_view.sql new file mode 100644 index 000000000000..88d68302d4cb --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.image_store_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. + +-- cloud.image_store_view source + + +DROP VIEW IF EXISTS `cloud`.`image_store_view`; + +CREATE VIEW `cloud`.`image_store_view` AS +select + `image_store`.`id` AS `id`, + `image_store`.`uuid` AS `uuid`, + `image_store`.`name` AS `name`, + `image_store`.`image_provider_name` AS `image_provider_name`, + `image_store`.`protocol` AS `protocol`, + `image_store`.`url` AS `url`, + `image_store`.`scope` AS `scope`, + `image_store`.`role` AS `role`, + `image_store`.`readonly` AS `readonly`, + `image_store`.`removed` AS `removed`, + `data_center`.`id` AS `data_center_id`, + `data_center`.`uuid` AS `data_center_uuid`, + `data_center`.`name` AS `data_center_name`, + `image_store_details`.`name` AS `detail_name`, + `image_store_details`.`value` AS `detail_value` +from + ((`image_store` +left join `data_center` on + ((`image_store`.`data_center_id` = `data_center`.`id`))) +left join `image_store_details` on + ((`image_store_details`.`store_id` = `image_store`.`id`))); diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.instance_group_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.instance_group_view.sql new file mode 100644 index 000000000000..8bdc81847181 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.instance_group_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.instance_group_view source + + +DROP VIEW IF EXISTS `cloud`.`instance_group_view`; + +CREATE VIEW `cloud`.`instance_group_view` AS +select + `instance_group`.`id` AS `id`, + `instance_group`.`uuid` AS `uuid`, + `instance_group`.`name` AS `name`, + `instance_group`.`removed` AS `removed`, + `instance_group`.`created` AS `created`, + `account`.`id` AS `account_id`, + `account`.`uuid` AS `account_uuid`, + `account`.`account_name` AS `account_name`, + `account`.`type` AS `account_type`, + `domain`.`id` AS `domain_id`, + `domain`.`uuid` AS `domain_uuid`, + `domain`.`name` AS `domain_name`, + `domain`.`path` AS `domain_path`, + `projects`.`id` AS `project_id`, + `projects`.`uuid` AS `project_uuid`, + `projects`.`name` AS `project_name` +from + (((`instance_group` +join `account` on + ((`instance_group`.`account_id` = `account`.`id`))) +join `domain` on + ((`account`.`domain_id` = `domain`.`id`))) +left join `projects` on + ((`projects`.`project_account_id` = `instance_group`.`account_id`))); diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.last_annotation_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.last_annotation_view.sql new file mode 100644 index 000000000000..f317fbacc4d5 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.last_annotation_view.sql @@ -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. + +-- cloud.last_annotation_view source + + +DROP VIEW IF EXISTS `cloud`.`last_annotation_view`; + +CREATE VIEW `cloud`.`last_annotation_view` AS +select + `annotations`.`uuid` AS `uuid`, + `annotations`.`annotation` AS `annotation`, + `annotations`.`entity_uuid` AS `entity_uuid`, + `annotations`.`entity_type` AS `entity_type`, + `annotations`.`user_uuid` AS `user_uuid`, + `annotations`.`created` AS `created`, + `annotations`.`removed` AS `removed` +from + `annotations` +where + `annotations`.`created` in ( + select + max(`annotations`.`created`) + from + `annotations` + where + (`annotations`.`removed` is null) + group by + `annotations`.`entity_uuid`); diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.mshost_peer_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.mshost_peer_view.sql new file mode 100644 index 000000000000..5f741449d85d --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.mshost_peer_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. + + +DROP VIEW IF EXISTS `cloud`.`mshost_peer_view`; + +CREATE VIEW `cloud`.`mshost_peer_view` AS +SELECT + `mshost_peer`.`id` AS `id`, + `mshost_peer`.`peer_state` AS `peer_state`, + `mshost_peer`.`last_update` AS `last_update`, + `owner_mshost`.`id` AS `owner_mshost_id`, + `owner_mshost`.`msid` AS `owner_mshost_msid`, + `owner_mshost`.`runid` AS `owner_mshost_runid`, + `owner_mshost`.`name` AS `owner_mshost_name`, + `owner_mshost`.`uuid` AS `owner_mshost_uuid`, + `owner_mshost`.`state` AS `owner_mshost_state`, + `owner_mshost`.`service_ip` AS `owner_mshost_service_ip`, + `owner_mshost`.`service_port` AS `owner_mshost_service_port`, + `peer_mshost`.`id` AS `peer_mshost_id`, + `peer_mshost`.`msid` AS `peer_mshost_msid`, + `peer_mshost`.`runid` AS `peer_mshost_runid`, + `peer_mshost`.`name` AS `peer_mshost_name`, + `peer_mshost`.`uuid` AS `peer_mshost_uuid`, + `peer_mshost`.`state` AS `peer_mshost_state`, + `peer_mshost`.`service_ip` AS `peer_mshost_service_ip`, + `peer_mshost`.`service_port` AS `peer_mshost_service_port` +FROM `cloud`.`mshost_peer` +LEFT JOIN `cloud`.`mshost` AS owner_mshost on `mshost_peer`.`owner_mshost` = `owner_mshost`.`id` +LEFT JOIN `cloud`.`mshost` AS peer_mshost on `mshost_peer`.`peer_mshost` = `peer_mshost`.`id`; diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.mshost_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.mshost_view.sql new file mode 100644 index 000000000000..9b68f170e38e --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.mshost_view.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. + +-- cloud.mshost_view source + + +DROP VIEW IF EXISTS `cloud`.`mshost_view`; + +CREATE VIEW `cloud`.`mshost_view` AS +select + `mshost`.`id` AS `id`, + `mshost`.`msid` AS `msid`, + `mshost`.`runid` AS `runid`, + `mshost`.`name` AS `name`, + `mshost`.`uuid` AS `uuid`, + `mshost`.`state` AS `state`, + `mshost`.`version` AS `version`, + `mshost`.`service_ip` AS `service_ip`, + `mshost`.`service_port` AS `service_port`, + `mshost`.`last_update` AS `last_update`, + `mshost`.`removed` AS `removed`, + `mshost`.`alert_count` AS `alert_count`, + `mshost_status`.`last_jvm_start` AS `last_jvm_start`, + `mshost_status`.`last_jvm_stop` AS `last_jvm_stop`, + `mshost_status`.`last_system_boot` AS `last_system_boot`, + `mshost_status`.`os_distribution` AS `os_distribution`, + `mshost_status`.`java_name` AS `java_name`, + `mshost_status`.`java_version` AS `java_version` +from + (`mshost` +left join `mshost_status` on + ((`mshost`.`uuid` = `mshost_status`.`ms_id`))); diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.network_offering_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.network_offering_view.sql index 8ba291e154cd..368566c32b32 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.network_offering_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.network_offering_view.sql @@ -59,8 +59,10 @@ SELECT `network_offerings`.`supports_public_access` AS `supports_public_access`, `network_offerings`.`supports_vm_autoscaling` AS `supports_vm_autoscaling`, `network_offerings`.`for_vpc` AS `for_vpc`, - `network_offerings`.`for_tungsten` AS `for_tungsten`, + `network_offerings`.`network_mode` AS `network_mode`, `network_offerings`.`service_package_id` AS `service_package_id`, + `network_offerings`.`routing_mode` AS `routing_mode`, + `network_offerings`.`specify_as_number` AS `specify_as_number`, GROUP_CONCAT(DISTINCT(domain.id)) AS domain_id, GROUP_CONCAT(DISTINCT(domain.uuid)) AS domain_uuid, GROUP_CONCAT(DISTINCT(domain.name)) AS domain_name, @@ -72,13 +74,9 @@ SELECT FROM `cloud`.`network_offerings` LEFT JOIN - `cloud`.`network_offering_details` AS `domain_details` ON `domain_details`.`network_offering_id` = `network_offerings`.`id` AND `domain_details`.`name`='domainid' + `cloud`.`domain` AS `domain` ON `domain`.id IN (SELECT value from `network_offering_details` where `name` = 'domainid' and `network_offering_id` = `network_offerings`.`id`) LEFT JOIN - `cloud`.`domain` AS `domain` ON FIND_IN_SET(`domain`.`id`, `domain_details`.`value`) - LEFT JOIN - `cloud`.`network_offering_details` AS `zone_details` ON `zone_details`.`network_offering_id` = `network_offerings`.`id` AND `zone_details`.`name`='zoneid' - LEFT JOIN - `cloud`.`data_center` AS `zone` ON FIND_IN_SET(`zone`.`id`, `zone_details`.`value`) + `cloud`.`data_center` AS `zone` ON `zone`.`id` IN (SELECT value from `network_offering_details` where `name` = 'zoneid' and `network_offering_id` = `network_offerings`.`id`) LEFT JOIN `cloud`.`network_offering_details` AS `offering_details` ON `offering_details`.`network_offering_id` = `network_offerings`.`id` AND `offering_details`.`name`='internetProtocol' GROUP BY diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.project_account_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.project_account_view.sql new file mode 100644 index 000000000000..c89618970cd2 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.project_account_view.sql @@ -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. + +-- cloud.project_account_view source + + +DROP VIEW IF EXISTS `cloud`.`project_account_view`; + +CREATE VIEW `cloud`.`project_account_view` AS +select + `project_account`.`id` AS `id`, + `account`.`id` AS `account_id`, + `account`.`uuid` AS `account_uuid`, + `account`.`account_name` AS `account_name`, + `account`.`type` AS `account_type`, + `user`.`id` AS `user_id`, + `user`.`uuid` AS `user_uuid`, + `user`.`username` AS `user_name`, + `project_account`.`account_role` AS `account_role`, + `project_role`.`id` AS `project_role_id`, + `project_role`.`uuid` AS `project_role_uuid`, + `projects`.`id` AS `project_id`, + `projects`.`uuid` AS `project_uuid`, + `projects`.`name` AS `project_name`, + `domain`.`id` AS `domain_id`, + `domain`.`uuid` AS `domain_uuid`, + `domain`.`name` AS `domain_name`, + `domain`.`path` AS `domain_path` +from + (((((`project_account` +join `account` on + ((`project_account`.`account_id` = `account`.`id`))) +join `domain` on + ((`account`.`domain_id` = `domain`.`id`))) +join `projects` on + ((`projects`.`id` = `project_account`.`project_id`))) +left join `project_role` on + ((`project_account`.`project_role_id` = `project_role`.`id`))) +left join `user` on + ((`project_account`.`user_id` = `user`.`id`))); diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.project_invitation_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.project_invitation_view.sql new file mode 100644 index 000000000000..fae35b9373e6 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.project_invitation_view.sql @@ -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. + +-- cloud.project_invitation_view source + + +DROP VIEW IF EXISTS `cloud`.`project_invitation_view`; + +CREATE VIEW `cloud`.`project_invitation_view` AS +select + `project_invitations`.`id` AS `id`, + `project_invitations`.`uuid` AS `uuid`, + `project_invitations`.`email` AS `email`, + `project_invitations`.`created` AS `created`, + `project_invitations`.`state` AS `state`, + `project_invitations`.`project_role_id` AS `project_role_id`, + `projects`.`id` AS `project_id`, + `projects`.`uuid` AS `project_uuid`, + `projects`.`name` AS `project_name`, + `account`.`id` AS `account_id`, + `account`.`uuid` AS `account_uuid`, + `account`.`account_name` AS `account_name`, + `account`.`type` AS `account_type`, + `user`.`id` AS `user_id`, + `domain`.`id` AS `domain_id`, + `domain`.`uuid` AS `domain_uuid`, + `domain`.`name` AS `domain_name`, + `domain`.`path` AS `domain_path` +from + ((((`project_invitations` +left join `account` on + ((`project_invitations`.`account_id` = `account`.`id`))) +left join `domain` on + ((`project_invitations`.`domain_id` = `domain`.`id`))) +left join `projects` on + ((`projects`.`id` = `project_invitations`.`project_id`))) +left join `user` on + ((`project_invitations`.`user_id` = `user`.`id`))); diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.project_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.project_view.sql new file mode 100644 index 000000000000..31461b1dd1ea --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.project_view.sql @@ -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. + +-- cloud.project_view source + + +DROP VIEW IF EXISTS `cloud`.`project_view`; + +CREATE VIEW `cloud`.`project_view` AS +select + `projects`.`id` AS `id`, + `projects`.`uuid` AS `uuid`, + `projects`.`name` AS `name`, + `projects`.`display_text` AS `display_text`, + `projects`.`state` AS `state`, + `projects`.`removed` AS `removed`, + `projects`.`created` AS `created`, + `projects`.`project_account_id` AS `project_account_id`, + `account`.`account_name` AS `owner`, + `pacct`.`account_id` AS `account_id`, + `pacct`.`user_id` AS `user_id`, + `domain`.`id` AS `domain_id`, + `domain`.`uuid` AS `domain_uuid`, + `domain`.`name` AS `domain_name`, + `domain`.`path` AS `domain_path` +from + ((((`projects` +join `domain` on + ((`projects`.`domain_id` = `domain`.`id`))) +join `project_account` on + (((`projects`.`id` = `project_account`.`project_id`) + and (`project_account`.`account_role` = 'Admin')))) +join `account` on + ((`account`.`id` = `project_account`.`account_id`))) +left join `project_account` `pacct` on + ((`projects`.`id` = `pacct`.`project_id`))); diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.resource_tag_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.resource_tag_view.sql new file mode 100644 index 000000000000..3d77d49f8701 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.resource_tag_view.sql @@ -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. + +-- cloud.resource_tag_view source + + +DROP VIEW IF EXISTS `cloud`.`resource_tag_view`; + +CREATE VIEW `cloud`.`resource_tag_view` AS +select + `resource_tags`.`id` AS `id`, + `resource_tags`.`uuid` AS `uuid`, + `resource_tags`.`key` AS `key`, + `resource_tags`.`value` AS `value`, + `resource_tags`.`resource_id` AS `resource_id`, + `resource_tags`.`resource_uuid` AS `resource_uuid`, + `resource_tags`.`resource_type` AS `resource_type`, + `resource_tags`.`customer` AS `customer`, + `account`.`id` AS `account_id`, + `account`.`uuid` AS `account_uuid`, + `account`.`account_name` AS `account_name`, + `account`.`type` AS `account_type`, + `domain`.`id` AS `domain_id`, + `domain`.`uuid` AS `domain_uuid`, + `domain`.`name` AS `domain_name`, + `domain`.`path` AS `domain_path`, + `projects`.`id` AS `project_id`, + `projects`.`uuid` AS `project_uuid`, + `projects`.`name` AS `project_name` +from + (((`resource_tags` +join `account` on + ((`resource_tags`.`account_id` = `account`.`id`))) +join `domain` on + ((`resource_tags`.`domain_id` = `domain`.`id`))) +left join `projects` on + ((`projects`.`project_account_id` = `resource_tags`.`account_id`))); diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.security_group_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.security_group_view.sql new file mode 100644 index 000000000000..3cae860c1c39 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.security_group_view.sql @@ -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. + +-- cloud.security_group_view source + + +DROP VIEW IF EXISTS `cloud`.`security_group_view`; + +CREATE VIEW `cloud`.`security_group_view` AS +select + `security_group`.`id` AS `id`, + `security_group`.`name` AS `name`, + `security_group`.`description` AS `description`, + `security_group`.`uuid` AS `uuid`, + `account`.`id` AS `account_id`, + `account`.`uuid` AS `account_uuid`, + `account`.`account_name` AS `account_name`, + `account`.`type` AS `account_type`, + `domain`.`id` AS `domain_id`, + `domain`.`uuid` AS `domain_uuid`, + `domain`.`name` AS `domain_name`, + `domain`.`path` AS `domain_path`, + `projects`.`id` AS `project_id`, + `projects`.`uuid` AS `project_uuid`, + `projects`.`name` AS `project_name`, + `security_group_rule`.`id` AS `rule_id`, + `security_group_rule`.`uuid` AS `rule_uuid`, + `security_group_rule`.`type` AS `rule_type`, + `security_group_rule`.`start_port` AS `rule_start_port`, + `security_group_rule`.`end_port` AS `rule_end_port`, + `security_group_rule`.`protocol` AS `rule_protocol`, + `security_group_rule`.`allowed_network_id` AS `rule_allowed_network_id`, + `security_group_rule`.`allowed_ip_cidr` AS `rule_allowed_ip_cidr`, + `security_group_rule`.`create_status` AS `rule_create_status`, + `resource_tags`.`id` AS `tag_id`, + `resource_tags`.`uuid` AS `tag_uuid`, + `resource_tags`.`key` AS `tag_key`, + `resource_tags`.`value` AS `tag_value`, + `resource_tags`.`domain_id` AS `tag_domain_id`, + `resource_tags`.`account_id` AS `tag_account_id`, + `resource_tags`.`resource_id` AS `tag_resource_id`, + `resource_tags`.`resource_uuid` AS `tag_resource_uuid`, + `resource_tags`.`resource_type` AS `tag_resource_type`, + `resource_tags`.`customer` AS `tag_customer`, + `async_job`.`id` AS `job_id`, + `async_job`.`uuid` AS `job_uuid`, + `async_job`.`job_status` AS `job_status`, + `async_job`.`account_id` AS `job_account_id` +from + ((((((`security_group` +left join `security_group_rule` on + ((`security_group`.`id` = `security_group_rule`.`security_group_id`))) +join `account` on + ((`security_group`.`account_id` = `account`.`id`))) +join `domain` on + ((`security_group`.`domain_id` = `domain`.`id`))) +left join `projects` on + ((`projects`.`project_account_id` = `security_group`.`account_id`))) +left join `resource_tags` on + (((`resource_tags`.`resource_id` = `security_group`.`id`) + and (`resource_tags`.`resource_type` = 'SecurityGroup')))) +left join `async_job` on + (((`async_job`.`instance_id` = `security_group`.`id`) + and (`async_job`.`instance_type` = 'SecurityGroup') + and (`async_job`.`job_status` = 0)))); diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.service_offering_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.service_offering_view.sql index e859af482b48..eb987af3ffb6 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.service_offering_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.service_offering_view.sql @@ -24,6 +24,7 @@ SELECT `service_offering`.`id` AS `id`, `service_offering`.`uuid` AS `uuid`, `service_offering`.`name` AS `name`, + `service_offering`.`state` AS `state`, `service_offering`.`display_text` AS `display_text`, `disk_offering`.`provisioning_type` AS `provisioning_type`, `service_offering`.`created` AS `created`, @@ -70,6 +71,20 @@ SELECT `service_offering`.`dynamic_scaling_enabled` AS `dynamic_scaling_enabled`, `service_offering`.`disk_offering_strictness` AS `disk_offering_strictness`, `vsphere_storage_policy`.`value` AS `vsphere_storage_policy`, + `lease_duration_details`.`value` AS `lease_duration`, + `lease_expiry_action_details`.`value` AS `lease_expiry_action`, + `gpu_card`.`id` AS `gpu_card_id`, + `gpu_card`.`uuid` AS `gpu_card_uuid`, + `gpu_card`.`name` AS `gpu_card_name`, + `vgpu_profile`.`id` AS `vgpu_profile_id`, + `vgpu_profile`.`uuid` AS `vgpu_profile_uuid`, + `vgpu_profile`.`name` AS `vgpu_profile_name`, + `vgpu_profile`.`video_ram` AS `vgpu_profile_video_ram`, + `vgpu_profile`.`max_heads` AS `vgpu_profile_max_heads`, + `vgpu_profile`.`max_resolution_x` AS `vgpu_profile_max_resolution_x`, + `vgpu_profile`.`max_resolution_y` AS `vgpu_profile_max_resolution_y`, + `service_offering`.`gpu_count` AS `gpu_count`, + `service_offering`.`gpu_display` AS `gpu_display`, GROUP_CONCAT(DISTINCT(domain.id)) AS domain_id, GROUP_CONCAT(DISTINCT(domain.uuid)) AS domain_uuid, GROUP_CONCAT(DISTINCT(domain.name)) AS domain_name, @@ -84,7 +99,11 @@ SELECT FROM `cloud`.`service_offering` INNER JOIN - `cloud`.`disk_offering_view` AS `disk_offering` ON service_offering.disk_offering_id = disk_offering.id + `cloud`.`disk_offering` ON service_offering.disk_offering_id = disk_offering.id + LEFT JOIN + `cloud`.`vgpu_profile` ON service_offering.vgpu_profile_id = vgpu_profile.id + LEFT JOIN + `cloud`.`gpu_card` ON vgpu_profile.card_id = gpu_card.id LEFT JOIN `cloud`.`service_offering_details` AS `domain_details` ON `domain_details`.`service_offering_id` = `service_offering`.`id` AND `domain_details`.`name`='domainid' LEFT JOIN @@ -108,7 +127,11 @@ FROM LEFT JOIN `cloud`.`service_offering_details` AS `vsphere_storage_policy` ON `vsphere_storage_policy`.`service_offering_id` = `service_offering`.`id` AND `vsphere_storage_policy`.`name` = 'storagepolicy' -WHERE - `service_offering`.`state`='Active' + LEFT JOIN + `cloud`.`service_offering_details` AS `lease_duration_details` ON `lease_duration_details`.`service_offering_id` = `service_offering`.`id` + AND `lease_duration_details`.`name` = 'leaseduration' + LEFT JOIN + `cloud`.`service_offering_details` AS `lease_expiry_action_details` ON `lease_expiry_action_details`.`service_offering_id` = `service_offering`.`id` + AND `lease_expiry_action_details`.`name` = 'leaseexpiryaction' GROUP BY `service_offering`.`id`; diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.shared_filesystem_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.shared_filesystem_view.sql new file mode 100644 index 000000000000..1f72babd1cec --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.shared_filesystem_view.sql @@ -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. + +-- VIEW `cloud`.`shared_filesystem_view`; + +DROP VIEW IF EXISTS `cloud`.`shared_filesystem_view`; + +CREATE VIEW `cloud`.`shared_filesystem_view` AS +SELECT + `shared_filesystem`.`id` AS `id`, + `shared_filesystem`.`uuid` AS `uuid`, + `shared_filesystem`.`name` AS `name`, + `shared_filesystem`.`description` AS `description`, + `shared_filesystem`.`state` AS `state`, + `shared_filesystem`.`fs_provider_name` AS `provider`, + `shared_filesystem`.`fs_type` AS `fs_type`, + `shared_filesystem`.`volume_id` AS `volume_id`, + `shared_filesystem`.`account_id` AS `account_id`, + `shared_filesystem`.`data_center_id` AS `zone_id`, + `zone`.`uuid` AS `zone_uuid`, + `zone`.`name` AS `zone_name`, + `instance`.`id` AS `instance_id`, + `instance`.`uuid` AS `instance_uuid`, + `instance`.`name` AS `instance_name`, + `instance`.`state` AS `instance_state`, + `volumes`.`size` AS `size`, + `volumes`.`uuid` AS `volume_uuid`, + `volumes`.`name` AS `volume_name`, + `volumes`.`provisioning_type` AS `provisioning_type`, + `volumes`.`format` AS `volume_format`, + `volumes`.`path` AS `volume_path`, + `volumes`.`chain_info` AS `volume_chain_info`, + `storage_pool`.`uuid` AS `pool_uuid`, + `storage_pool`.`name` AS `pool_name`, + `account`.`account_name` AS `account_name`, + `project`.`uuid` AS `project_uuid`, + `project`.`name` AS `project_name`, + `domain`.`uuid` AS `domain_uuid`, + `domain`.`name` AS `domain_name`, + `domain`.`path` AS `domain_path`, + `service_offering`.`uuid` AS `service_offering_uuid`, + `service_offering`.`name` AS `service_offering_name`, + `disk_offering`.`uuid` AS `disk_offering_uuid`, + `disk_offering`.`name` AS `disk_offering_name`, + `disk_offering`.`display_text` AS `disk_offering_display_text`, + `disk_offering`.`disk_size` AS `disk_offering_size`, + `disk_offering`.`customized` AS `disk_offering_custom` +FROM + `cloud`.`shared_filesystem` + LEFT JOIN + `cloud`.`data_center` AS `zone` ON `shared_filesystem`.`data_center_id` = `zone`.`id` + LEFT JOIN + `cloud`.`vm_instance` AS `instance` ON `shared_filesystem`.`vm_id` = `instance`.`id` + LEFT JOIN + `cloud`.`volumes` AS `volumes` ON `shared_filesystem`.`volume_id` = `volumes`.`id` + LEFT JOIN + `cloud`.`storage_pool` AS `storage_pool` ON `volumes`.`pool_id` = `storage_pool`.`id` + LEFT JOIN + `cloud`.`account` AS `account` ON `shared_filesystem`.`account_id` = `account`.`id` + LEFT JOIN + `cloud`.`projects` AS `project` ON `project`.`project_account_id` = `account`.`id` + LEFT JOIN + `cloud`.`domain` AS `domain` ON `shared_filesystem`.`domain_id` = `domain`.`id` + LEFT JOIN + `cloud`.`service_offering` AS `service_offering` ON `shared_filesystem`.`service_offering_id` = `service_offering`.`id` + LEFT JOIN + `cloud`.`disk_offering` AS `disk_offering` ON `volumes`.`disk_offering_id` = `disk_offering`.`id` +GROUP BY + `shared_filesystem`.`id`; diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.snapshot_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.snapshot_view.sql index c6b8d6b4d05e..d0eddc1fc4b3 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.snapshot_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.snapshot_view.sql @@ -48,6 +48,7 @@ SELECT `volumes`.`uuid` AS `volume_uuid`, `volumes`.`name` AS `volume_name`, `volumes`.`volume_type` AS `volume_type`, + `volumes`.`state` AS `volume_state`, `volumes`.`size` AS `volume_size`, `data_center`.`id` AS `data_center_id`, `data_center`.`uuid` AS `data_center_uuid`, diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.storage_pool_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.storage_pool_view.sql index e6cc94582088..641017bdd5ba 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.storage_pool_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.storage_pool_view.sql @@ -31,7 +31,9 @@ SELECT `storage_pool`.`created` AS `created`, `storage_pool`.`removed` AS `removed`, `storage_pool`.`capacity_bytes` AS `capacity_bytes`, + `storage_pool`.`used_bytes` AS `used_bytes`, `storage_pool`.`capacity_iops` AS `capacity_iops`, + `storage_pool`.`used_iops` AS `used_iops`, `storage_pool`.`scope` AS `scope`, `storage_pool`.`hypervisor` AS `hypervisor`, `storage_pool`.`storage_provider_name` AS `storage_provider_name`, @@ -49,6 +51,7 @@ SELECT `host_pod_ref`.`name` AS `pod_name`, `storage_pool_tags`.`tag` AS `tag`, `storage_pool_tags`.`is_tag_a_rule` AS `is_tag_a_rule`, + `storage_pool_and_access_group_map`.`storage_access_group` AS `storage_access_group`, `op_host_capacity`.`used_capacity` AS `disk_used_capacity`, `op_host_capacity`.`reserved_capacity` AS `disk_reserved_capacity`, `async_job`.`id` AS `job_id`, @@ -56,13 +59,16 @@ SELECT `async_job`.`job_status` AS `job_status`, `async_job`.`account_id` AS `job_account_id` FROM - ((((((`cloud`.`storage_pool` - LEFT JOIN `cloud`.`cluster` ON ((`storage_pool`.`cluster_id` = `cluster`.`id`))) - LEFT JOIN `cloud`.`data_center` ON ((`storage_pool`.`data_center_id` = `data_center`.`id`))) - LEFT JOIN `cloud`.`host_pod_ref` ON ((`storage_pool`.`pod_id` = `host_pod_ref`.`id`))) - LEFT JOIN `cloud`.`storage_pool_tags` ON (((`storage_pool_tags`.`pool_id` = `storage_pool`.`id`)))) - LEFT JOIN `cloud`.`op_host_capacity` ON (((`storage_pool`.`id` = `op_host_capacity`.`host_id`) - AND (`op_host_capacity`.`capacity_type` IN (3 , 9))))) - LEFT JOIN `cloud`.`async_job` ON (((`async_job`.`instance_id` = `storage_pool`.`id`) - AND (`async_job`.`instance_type` = 'StoragePool') - AND (`async_job`.`job_status` = 0)))); + `cloud`.`storage_pool` + LEFT JOIN `cloud`.`cluster` ON `storage_pool`.`cluster_id` = `cluster`.`id` + LEFT JOIN `cloud`.`data_center` ON `storage_pool`.`data_center_id` = `data_center`.`id` + LEFT JOIN `cloud`.`host_pod_ref` ON `storage_pool`.`pod_id` = `host_pod_ref`.`id` + LEFT JOIN `cloud`.`storage_pool_tags` ON `storage_pool_tags`.`pool_id` = `storage_pool`.`id` + LEFT JOIN `cloud`.`storage_pool_and_access_group_map` ON `storage_pool_and_access_group_map`.`pool_id` = `storage_pool`.`id` + LEFT JOIN `cloud`.`op_host_capacity` + ON `storage_pool`.`id` = `op_host_capacity`.`host_id` + AND `op_host_capacity`.`capacity_type` IN (3, 9) + LEFT JOIN `cloud`.`async_job` + ON `async_job`.`instance_id` = `storage_pool`.`id` + AND `async_job`.`instance_type` = 'StoragePool' + AND `async_job`.`job_status` = 0; diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.template_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.template_view.sql index 40b416b16de4..76a8be16bda6 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.template_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.template_view.sql @@ -41,6 +41,7 @@ SELECT `vm_template`.`guest_os_id` AS `guest_os_id`, `guest_os`.`uuid` AS `guest_os_uuid`, `guest_os`.`display_name` AS `guest_os_name`, + `guest_os`.`category_id` AS `guest_os_category_id`, `vm_template`.`bootable` AS `bootable`, `vm_template`.`prepopulate` AS `prepopulate`, `vm_template`.`cross_zones` AS `cross_zones`, @@ -50,6 +51,7 @@ SELECT `vm_template`.`sort_key` AS `sort_key`, `vm_template`.`removed` AS `removed`, `vm_template`.`enable_sshkey` AS `enable_sshkey`, + `vm_template`.`arch` AS `arch`, `parent_template`.`id` AS `parent_template_id`, `parent_template`.`uuid` AS `parent_template_uuid`, `source_template`.`id` AS `source_template_id`, @@ -99,11 +101,15 @@ SELECT IFNULL(`data_center`.`id`, 0)) AS `temp_zone_pair`, `vm_template`.`direct_download` AS `direct_download`, `vm_template`.`deploy_as_is` AS `deploy_as_is`, + `vm_template`.`for_cks` AS `for_cks`, `user_data`.`id` AS `user_data_id`, `user_data`.`uuid` AS `user_data_uuid`, `user_data`.`name` AS `user_data_name`, `user_data`.`params` AS `user_data_params`, - `vm_template`.`user_data_link_policy` AS `user_data_policy` + `vm_template`.`user_data_link_policy` AS `user_data_policy`, + `extension`.`id` AS `extension_id`, + `extension`.`uuid` AS `extension_uuid`, + `extension`.`name` AS `extension_name` FROM (((((((((((((`vm_template` JOIN `guest_os` ON ((`guest_os`.`id` = `vm_template`.`guest_os_id`))) @@ -126,6 +132,7 @@ FROM OR (`template_zone_ref`.`zone_id` = `data_center`.`id`)))) LEFT JOIN `launch_permission` ON ((`launch_permission`.`template_id` = `vm_template`.`id`))) LEFT JOIN `user_data` ON ((`user_data`.`id` = `vm_template`.`user_data_id`)) + LEFT JOIN `extension` ON ((`extension`.`id` = `vm_template`.`extension_id`)) LEFT JOIN `resource_tags` ON (((`resource_tags`.`resource_id` = `vm_template`.`id`) AND ((`resource_tags`.`resource_type` = 'Template') OR (`resource_tags`.`resource_type` = 'ISO'))))); diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.user_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.user_view.sql index 7eedc03712b6..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, @@ -39,6 +37,7 @@ select user.incorrect_login_attempts, user.source, user.default, + user.api_key_access, account.id account_id, account.uuid account_uuid, account.account_name account_name, 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 7a057dc0330b..94bc8640fd54 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 @@ -25,6 +25,7 @@ SELECT `vm_instance`.`name` AS `name`, `user_vm`.`display_name` AS `display_name`, `user_vm`.`user_data` AS `user_data`, + `user_vm`.`user_vm_type` AS `user_vm_type`, `account`.`id` AS `account_id`, `account`.`uuid` AS `account_uuid`, `account`.`account_name` AS `account_name`, @@ -53,6 +54,7 @@ SELECT `vm_instance`.`instance_name` AS `instance_name`, `vm_instance`.`guest_os_id` AS `guest_os_id`, `vm_instance`.`display_vm` AS `display_vm`, + `vm_instance`.`delete_protection` AS `delete_protection`, `guest_os`.`uuid` AS `guest_os_uuid`, `vm_instance`.`pod_id` AS `pod_id`, `host_pod_ref`.`uuid` AS `pod_uuid`, @@ -74,12 +76,14 @@ SELECT `vm_template`.`uuid` AS `template_uuid`, `vm_template`.`name` AS `template_name`, `vm_template`.`type` AS `template_type`, + `vm_template`.`format` AS `template_format`, `vm_template`.`display_text` AS `template_display_text`, `vm_template`.`enable_password` AS `password_enabled`, `iso`.`id` AS `iso_id`, `iso`.`uuid` AS `iso_uuid`, `iso`.`name` AS `iso_name`, `iso`.`display_text` AS `iso_display_text`, + `vm_template`.`arch` AS `arch`, `service_offering`.`id` AS `service_offering_id`, `service_offering`.`uuid` AS `service_offering_uuid`, `disk_offering`.`uuid` AS `disk_offering_uuid`, @@ -99,6 +103,17 @@ SELECT `backup_offering`.`uuid` AS `backup_offering_uuid`, `backup_offering`.`id` AS `backup_offering_id`, `service_offering`.`name` AS `service_offering_name`, + `service_offering`.`vgpu_profile_id` AS `vgpu_profile_id`, + `vgpu_profile`.`uuid` AS `vgpu_profile_uuid`, + `vgpu_profile`.`name` AS `vgpu_profile_name`, + `vgpu_profile`.`video_ram` AS `vgpu_profile_video_ram`, + `vgpu_profile`.`max_heads` AS `vgpu_profile_max_heads`, + `vgpu_profile`.`max_resolution_x` AS `vgpu_profile_max_resolution_x`, + `vgpu_profile`.`max_resolution_y` AS `vgpu_profile_max_resolution_y`, + `gpu_card`.`id` AS `gpu_card_id`, + `gpu_card`.`uuid` AS `gpu_card_uuid`, + `gpu_card`.`name` AS `gpu_card_name`, + `service_offering`.`gpu_count` AS `gpu_count`, `disk_offering`.`name` AS `disk_offering_name`, `backup_offering`.`name` AS `backup_offering_name`, `storage_pool`.`id` AS `pool_id`, @@ -165,9 +180,12 @@ SELECT `user_data`.`uuid` AS `user_data_uuid`, `user_data`.`name` AS `user_data_name`, `user_vm`.`user_data_details` AS `user_data_details`, - `vm_template`.`user_data_link_policy` AS `user_data_policy` + `vm_template`.`user_data_link_policy` AS `user_data_policy`, + `lease_expiry_date`.`value` AS `lease_expiry_date`, + `lease_expiry_action`.`value` AS `lease_expiry_action`, + `lease_action_execution`.`value` AS `lease_action_execution` FROM - (((((((((((((((((((((((((((((((((((`user_vm` + (((((((((((((((((((((((((((((((((((((`user_vm` JOIN `vm_instance` ON (((`vm_instance`.`id` = `user_vm`.`id`) AND ISNULL(`vm_instance`.`removed`)))) JOIN `account` ON ((`vm_instance`.`account_id` = `account`.`id`))) @@ -185,6 +203,8 @@ FROM LEFT JOIN `service_offering` ON ((`vm_instance`.`service_offering_id` = `service_offering`.`id`))) LEFT JOIN `disk_offering` `svc_disk_offering` ON ((`volumes`.`disk_offering_id` = `svc_disk_offering`.`id`))) LEFT JOIN `disk_offering` ON ((`volumes`.`disk_offering_id` = `disk_offering`.`id`))) + LEFT JOIN `vgpu_profile` ON ((`service_offering`.`vgpu_profile_id` = `vgpu_profile`.`id`))) + LEFT JOIN `gpu_card` ON ((`vgpu_profile`.`card_id` = `gpu_card`.`id`))) LEFT JOIN `backup_offering` ON ((`vm_instance`.`backup_offering_id` = `backup_offering`.`id`))) LEFT JOIN `storage_pool` ON ((`volumes`.`pool_id` = `storage_pool`.`id`))) LEFT JOIN `security_group_vm_map` ON ((`vm_instance`.`id` = `security_group_vm_map`.`instance_id`))) @@ -195,8 +215,8 @@ FROM LEFT JOIN `networks` ON ((`nics`.`network_id` = `networks`.`id`))) LEFT JOIN `vpc` ON (((`networks`.`vpc_id` = `vpc`.`id`) AND ISNULL(`vpc`.`removed`)))) - LEFT JOIN `user_ip_address` ON ((`user_ip_address`.`vm_id` = `vm_instance`.`id`))) - LEFT JOIN `user_vm_details` `ssh_details` ON (((`ssh_details`.`vm_id` = `vm_instance`.`id`) + LEFT JOIN `user_ip_address` FORCE INDEX(`fk_user_ip_address__vm_id`) ON ((`user_ip_address`.`vm_id` = `vm_instance`.`id`))) + LEFT JOIN `vm_instance_details` `ssh_details` ON (((`ssh_details`.`vm_id` = `vm_instance`.`id`) AND (`ssh_details`.`name` = 'SSH.KeyPairNames')))) LEFT JOIN `resource_tags` ON (((`resource_tags`.`resource_id` = `vm_instance`.`id`) AND (`resource_tags`.`resource_type` = 'UserVm')))) @@ -207,9 +227,15 @@ FROM LEFT JOIN `affinity_group` ON ((`affinity_group_vm_map`.`affinity_group_id` = `affinity_group`.`id`))) LEFT JOIN `autoscale_vmgroup_vm_map` ON ((`autoscale_vmgroup_vm_map`.`instance_id` = `vm_instance`.`id`))) LEFT JOIN `autoscale_vmgroups` ON ((`autoscale_vmgroup_vm_map`.`vmgroup_id` = `autoscale_vmgroups`.`id`))) - LEFT JOIN `user_vm_details` `custom_cpu` ON (((`custom_cpu`.`vm_id` = `vm_instance`.`id`) + LEFT JOIN `vm_instance_details` `custom_cpu` ON (((`custom_cpu`.`vm_id` = `vm_instance`.`id`) AND (`custom_cpu`.`name` = 'CpuNumber')))) - LEFT JOIN `user_vm_details` `custom_speed` ON (((`custom_speed`.`vm_id` = `vm_instance`.`id`) + LEFT JOIN `vm_instance_details` `custom_speed` ON (((`custom_speed`.`vm_id` = `vm_instance`.`id`) AND (`custom_speed`.`name` = 'CpuSpeed')))) - LEFT JOIN `user_vm_details` `custom_ram_size` ON (((`custom_ram_size`.`vm_id` = `vm_instance`.`id`) - AND (`custom_ram_size`.`name` = 'memory')))); + LEFT JOIN `vm_instance_details` `custom_ram_size` ON (((`custom_ram_size`.`vm_id` = `vm_instance`.`id`) + AND (`custom_ram_size`.`name` = 'memory'))) + LEFT JOIN `vm_instance_details` `lease_expiry_date` ON ((`lease_expiry_date`.`vm_id` = `vm_instance`.`id`) + AND (`lease_expiry_date`.`name` = 'leaseexpirydate')) + LEFT JOIN `vm_instance_details` `lease_action_execution` ON ((`lease_action_execution`.`vm_id` = `vm_instance`.`id`) + AND (`lease_action_execution`.`name` = 'leaseactionexecution')) + LEFT JOIN `vm_instance_details` `lease_expiry_action` ON (((`lease_expiry_action`.`vm_id` = `vm_instance`.`id`) + AND (`lease_expiry_action`.`name` = 'leaseexpiryaction')))); diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.volume_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.volume_view.sql new file mode 100644 index 000000000000..ffeb93e8fa7a --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.volume_view.sql @@ -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. + +-- VIEW `cloud`.`volume_view`; + +DROP VIEW IF EXISTS `cloud`.`volume_view`; + +CREATE VIEW `cloud`.`volume_view` AS +SELECT + `volumes`.`id` AS `id`, + `volumes`.`uuid` AS `uuid`, + `volumes`.`name` AS `name`, + `volumes`.`device_id` AS `device_id`, + `volumes`.`volume_type` AS `volume_type`, + `volumes`.`provisioning_type` AS `provisioning_type`, + `volumes`.`size` AS `size`, + `volumes`.`min_iops` AS `min_iops`, + `volumes`.`max_iops` AS `max_iops`, + `volumes`.`created` AS `created`, + `volumes`.`state` AS `state`, + `volumes`.`attached` AS `attached`, + `volumes`.`removed` AS `removed`, + `volumes`.`display_volume` AS `display_volume`, + `volumes`.`format` AS `format`, + `volumes`.`path` AS `path`, + `volumes`.`chain_info` AS `chain_info`, + `volumes`.`external_uuid` AS `external_uuid`, + `volumes`.`encrypt_format` AS `encrypt_format`, + `volumes`.`delete_protection` AS `delete_protection`, + `account`.`id` AS `account_id`, + `account`.`uuid` AS `account_uuid`, + `account`.`account_name` AS `account_name`, + `account`.`type` AS `account_type`, + `domain`.`id` AS `domain_id`, + `domain`.`uuid` AS `domain_uuid`, + `domain`.`name` AS `domain_name`, + `domain`.`path` AS `domain_path`, + `projects`.`id` AS `project_id`, + `projects`.`uuid` AS `project_uuid`, + `projects`.`name` AS `project_name`, + `data_center`.`id` AS `data_center_id`, + `data_center`.`uuid` AS `data_center_uuid`, + `data_center`.`name` AS `data_center_name`, + `data_center`.`networktype` AS `data_center_type`, + `vm_instance`.`id` AS `vm_id`, + `vm_instance`.`uuid` AS `vm_uuid`, + `vm_instance`.`name` AS `vm_name`, + `vm_instance`.`state` AS `vm_state`, + `vm_instance`.`vm_type` AS `vm_type`, + `user_vm`.`display_name` AS `vm_display_name`, + `volume_store_ref`.`size` AS `volume_store_size`, + `volume_store_ref`.`download_pct` AS `download_pct`, + `volume_store_ref`.`download_state` AS `download_state`, + `volume_store_ref`.`error_str` AS `error_str`, + `volume_store_ref`.`created` AS `created_on_store`, + `disk_offering`.`id` AS `disk_offering_id`, + `disk_offering`.`uuid` AS `disk_offering_uuid`, + `disk_offering`.`name` AS `disk_offering_name`, + `disk_offering`.`display_text` AS `disk_offering_display_text`, + `disk_offering`.`use_local_storage` AS `use_local_storage`, + `service_offering`.`system_use` AS `system_use`, + `disk_offering`.`bytes_read_rate` AS `bytes_read_rate`, + `disk_offering`.`bytes_write_rate` AS `bytes_write_rate`, + `disk_offering`.`iops_read_rate` AS `iops_read_rate`, + `disk_offering`.`iops_write_rate` AS `iops_write_rate`, + `disk_offering`.`cache_mode` AS `cache_mode`, + `storage_pool`.`id` AS `pool_id`, + `storage_pool`.`uuid` AS `pool_uuid`, + `storage_pool`.`name` AS `pool_name`, + `cluster`.`id` AS `cluster_id`, + `cluster`.`name` AS `cluster_name`, + `cluster`.`uuid` AS `cluster_uuid`, + `cluster`.`hypervisor_type` AS `hypervisor_type`, + `vm_template`.`id` AS `template_id`, + `vm_template`.`uuid` AS `template_uuid`, + `vm_template`.`extractable` AS `extractable`, + `vm_template`.`type` AS `template_type`, + `vm_template`.`name` AS `template_name`, + `vm_template`.`display_text` AS `template_display_text`, + `iso`.`id` AS `iso_id`, + `iso`.`uuid` AS `iso_uuid`, + `iso`.`name` AS `iso_name`, + `iso`.`display_text` AS `iso_display_text`, + `resource_tags`.`id` AS `tag_id`, + `resource_tags`.`uuid` AS `tag_uuid`, + `resource_tags`.`key` AS `tag_key`, + `resource_tags`.`value` AS `tag_value`, + `resource_tags`.`domain_id` AS `tag_domain_id`, + `resource_tags`.`account_id` AS `tag_account_id`, + `resource_tags`.`resource_id` AS `tag_resource_id`, + `resource_tags`.`resource_uuid` AS `tag_resource_uuid`, + `resource_tags`.`resource_type` AS `tag_resource_type`, + `resource_tags`.`customer` AS `tag_customer`, + `async_job`.`id` AS `job_id`, + `async_job`.`uuid` AS `job_uuid`, + `async_job`.`job_status` AS `job_status`, + `async_job`.`account_id` AS `job_account_id`, + `host_pod_ref`.`id` AS `pod_id`, + `host_pod_ref`.`uuid` AS `pod_uuid`, + `host_pod_ref`.`name` AS `pod_name`, + `resource_tag_account`.`account_name` AS `tag_account_name`, + `resource_tag_domain`.`uuid` AS `tag_domain_uuid`, + `resource_tag_domain`.`name` AS `tag_domain_name` +FROM + ((((((((((((((((((`volumes` +JOIN `account`ON + ((`volumes`.`account_id` = `account`.`id`))) +JOIN `domain`ON + ((`volumes`.`domain_id` = `domain`.`id`))) +LEFT JOIN `projects`ON + ((`projects`.`project_account_id` = `account`.`id`))) +LEFT JOIN `data_center`ON + ((`volumes`.`data_center_id` = `data_center`.`id`))) +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 + ((`volumes`.`id` = `volume_store_ref`.`volume_id`))) +LEFT JOIN `service_offering`ON + ((`vm_instance`.`service_offering_id` = `service_offering`.`id`))) +LEFT JOIN `disk_offering`ON + ((`volumes`.`disk_offering_id` = `disk_offering`.`id`))) +LEFT JOIN `storage_pool`ON + ((`volumes`.`pool_id` = `storage_pool`.`id`))) +LEFT JOIN `host_pod_ref`ON + ((`storage_pool`.`pod_id` = `host_pod_ref`.`id`))) +LEFT JOIN `cluster`ON + ((`storage_pool`.`cluster_id` = `cluster`.`id`))) +LEFT JOIN `vm_template`ON + ((`volumes`.`template_id` = `vm_template`.`id`))) +LEFT JOIN `vm_template` `iso`ON + ((`iso`.`id` = `volumes`.`iso_id`))) +LEFT JOIN `resource_tags`ON + (((`resource_tags`.`resource_id` = `volumes`.`id`) + and (`resource_tags`.`resource_type` = 'Volume')))) +LEFT JOIN `async_job`ON + (((`async_job`.`instance_id` = `volumes`.`id`) + and (`async_job`.`instance_type` = 'Volume') + and (`async_job`.`job_status` = 0)))) +LEFT JOIN `account` `resource_tag_account`ON + ((`resource_tag_account`.`id` = `resource_tags`.`account_id`))) +LEFT JOIN `domain` `resource_tag_domain`ON + ((`resource_tag_domain`.`id` = `resource_tags`.`domain_id`))); 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 new file mode 100644 index 000000000000..751d8f91a259 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.vpc_offering_view.sql @@ -0,0 +1,65 @@ +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. + +-- cloud.vpc_offering_view source + +DROP VIEW IF EXISTS `cloud`.`vpc_offering_view`; + +CREATE VIEW `cloud`.`vpc_offering_view` AS +select + `vpc_offerings`.`id` AS `id`, + `vpc_offerings`.`uuid` AS `uuid`, + `vpc_offerings`.`name` AS `name`, + `vpc_offerings`.`unique_name` AS `unique_name`, + `vpc_offerings`.`display_text` AS `display_text`, + `vpc_offerings`.`state` AS `state`, + `vpc_offerings`.`default` AS `default`, + `vpc_offerings`.`network_mode` AS `network_mode`, + `vpc_offerings`.`created` AS `created`, + `vpc_offerings`.`removed` AS `removed`, + `vpc_offerings`.`service_offering_id` AS `service_offering_id`, + `vpc_offerings`.`supports_distributed_router` AS `supports_distributed_router`, + `vpc_offerings`.`supports_region_level_vpc` AS `supports_region_level_vpc`, + `vpc_offerings`.`redundant_router_service` AS `redundant_router_service`, + `vpc_offerings`.`sort_key` AS `sort_key`, + `vpc_offerings`.`routing_mode` AS `routing_mode`, + `vpc_offerings`.`specify_as_number` AS `specify_as_number`, + 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`, + group_concat(distinct `domain`.`path` separator ',') AS `domain_path`, + group_concat(distinct `zone`.`id` separator ',') AS `zone_id`, + group_concat(distinct `zone`.`uuid` separator ',') AS `zone_uuid`, + group_concat(distinct `zone`.`name` separator ',') AS `zone_name`, + `offering_details`.`value` AS `internet_protocol` +from + (((((`vpc_offerings` +left join `vpc_offering_details` `domain_details` on + (((`domain_details`.`offering_id` = `vpc_offerings`.`id`) + and (`domain_details`.`name` = 'domainid')))) +left join `domain` on + ((0 <> find_in_set(`domain`.`id`, `domain_details`.`value`)))) +left join `vpc_offering_details` `zone_details` on + (((`zone_details`.`offering_id` = `vpc_offerings`.`id`) + and (`zone_details`.`name` = 'zoneid')))) +left join `data_center` `zone` on + ((0 <> find_in_set(`zone`.`id`, `zone_details`.`value`)))) +left join `vpc_offering_details` `offering_details` on + (((`offering_details`.`offering_id` = `vpc_offerings`.`id`) + and (`offering_details`.`name` = 'internetprotocol')))) +group by + `vpc_offerings`.`id`; diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.webhook_delivery_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.webhook_delivery_view.sql new file mode 100644 index 000000000000..54ba52fba4a6 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.webhook_delivery_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. + +-- VIEW `cloud`.`webhook_delivery_view`; + +DROP VIEW IF EXISTS `cloud`.`webhook_delivery_view`; +CREATE VIEW `cloud`.`webhook_delivery_view` AS + SELECT + webhook_delivery.id, + webhook_delivery.uuid, + webhook_delivery.headers, + webhook_delivery.payload, + webhook_delivery.success, + webhook_delivery.response, + webhook_delivery.start_time, + webhook_delivery.end_time, + event.id event_id, + event.uuid event_uuid, + event.type event_type, + webhook.id webhook_id, + webhook.uuid webhook_uuid, + webhook.name webhook_name, + mshost.id mshost_id, + mshost.uuid mshost_uuid, + mshost.msid mshost_msid, + mshost.name mshost_name + FROM + `cloud`.`webhook_delivery` + INNER JOIN + `cloud`.`event` ON webhook_delivery.event_id = event.id + INNER JOIN + `cloud`.`webhook` ON webhook_delivery.webhook_id = webhook.id + LEFT JOIN + `cloud`.`mshost` ON mshost.msid = webhook_delivery.mshost_msid; diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.webhook_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.webhook_view.sql new file mode 100644 index 000000000000..443463eec4bd --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.webhook_view.sql @@ -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. + +-- VIEW `cloud`.`webhook_view`; + +DROP VIEW IF EXISTS `cloud`.`webhook_view`; +CREATE VIEW `cloud`.`webhook_view` AS + SELECT + webhook.id, + webhook.uuid, + webhook.name, + webhook.description, + webhook.state, + webhook.payload_url, + webhook.secret_key, + webhook.ssl_verification, + webhook.scope, + webhook.created, + webhook.removed, + account.id account_id, + account.uuid account_uuid, + account.account_name account_name, + account.type account_type, + domain.id domain_id, + domain.uuid domain_uuid, + domain.name domain_name, + domain.path domain_path, + projects.id project_id, + projects.uuid project_uuid, + projects.name project_name + FROM + `cloud`.`webhook` + INNER JOIN + `cloud`.`account` ON webhook.account_id = account.id + INNER JOIN + `cloud`.`domain` ON webhook.domain_id = domain.id + LEFT JOIN + `cloud`.`projects` ON projects.project_account_id = webhook.account_id; diff --git a/engine/schema/src/test/java/com/cloud/capacity/dao/CapacityDaoImplTest.java b/engine/schema/src/test/java/com/cloud/capacity/dao/CapacityDaoImplTest.java new file mode 100644 index 000000000000..f9528d5d57ff --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/capacity/dao/CapacityDaoImplTest.java @@ -0,0 +1,553 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES 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.capacity.dao; + +import com.cloud.capacity.CapacityVO; +import com.cloud.host.Host; +import com.cloud.utils.Pair; +import com.cloud.utils.Ternary; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.TransactionLegacy; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class CapacityDaoImplTest { + @Spy + @InjectMocks + CapacityDaoImpl capacityDao = new CapacityDaoImpl(); + + @Mock + private CapacityVO mockEntity; + + @Mock + private TransactionLegacy txn; + @Mock + private PreparedStatement pstmt; + @Mock + private ResultSet resultSet; + private MockedStatic mockedTransactionLegacy; + + private SearchBuilder searchBuilder; + private SearchCriteria searchCriteria; + private List capacityTypes; + private List expectedCapacities; + + @Before + public void setUp() { + searchBuilder = mock(SearchBuilder.class); + CapacityVO capacityVO = mock(CapacityVO.class); + when(searchBuilder.entity()).thenReturn(capacityVO); + searchCriteria = mock(SearchCriteria.class); + doReturn(searchBuilder).when(capacityDao).createSearchBuilder(); + when(searchBuilder.create()).thenReturn(searchCriteria); + + mockedTransactionLegacy = Mockito.mockStatic(TransactionLegacy.class); + mockedTransactionLegacy.when(TransactionLegacy::currentTxn).thenReturn(txn); + + // Setup common test data + capacityTypes = Arrays.asList((short) 1, (short) 2, (short) 3); + expectedCapacities = Arrays.asList(mock(CapacityVO.class), mock(CapacityVO.class)); + doReturn(expectedCapacities).when(capacityDao).listBy(searchCriteria); + } + + private CapacityVO createMockCapacityVO(Long id) { + CapacityVO capacity = mock(CapacityVO.class); + when(capacity.getId()).thenReturn(id); + return capacity; + } + + @After + public void tearDown() { + if (mockedTransactionLegacy != null) { + mockedTransactionLegacy.close(); + } + } + + @Test + public void testListByHostIdTypes() { + // Prepare inputs + Long hostId = 1L; + List capacityTypes = Arrays.asList((short)1, (short)2); + CapacityVO capacity1 = new CapacityVO(); + CapacityVO capacity2 = new CapacityVO(); + List mockResult = Arrays.asList(capacity1, capacity2); + doReturn(mockResult).when(capacityDao).listBy(any(SearchCriteria.class)); + List result = capacityDao.listByHostIdTypes(hostId, capacityTypes); + verify(searchBuilder).and(eq("hostId"), any(), eq(SearchCriteria.Op.EQ)); + verify(searchBuilder).and(eq("type"), any(), eq(SearchCriteria.Op.IN)); + verify(searchBuilder).done(); + verify(searchCriteria).setParameters("hostId", hostId); + verify(searchCriteria).setParameters("type", capacityTypes.toArray()); + verify(capacityDao).listBy(searchCriteria); + assertEquals(2, result.size()); + assertSame(capacity1, result.get(0)); + assertSame(capacity2, result.get(1)); + } + + @Test + public void testListByHostIdTypesEmptyResult() { + Long hostId = 1L; + List capacityTypes = Arrays.asList((short)1, (short)2); + doReturn(Collections.emptyList()).when(capacityDao).listBy(any(SearchCriteria.class)); + List result = capacityDao.listByHostIdTypes(hostId, capacityTypes); + verify(searchBuilder).and(Mockito.eq("hostId"), any(), eq(SearchCriteria.Op.EQ)); + verify(searchBuilder).and(eq("type"), any(), eq(SearchCriteria.Op.IN)); + verify(searchBuilder).done(); + verify(searchCriteria).setParameters("hostId", hostId); + verify(searchCriteria).setParameters("type", capacityTypes.toArray()); + verify(capacityDao).listBy(searchCriteria); + assertTrue(result.isEmpty()); + } + + @Test + public void testListClustersCrossingThresholdEmptyResult() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + List result = capacityDao.listClustersCrossingThreshold((short)1, 1L, "cpu.threshold", 5000L); + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + @Test + public void testFindCapacityByZoneAndHostTagNoResults() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + + Ternary result = capacityDao.findCapacityByZoneAndHostTag(1L, "host-tag"); + assertNotNull(result); + assertEquals(Long.valueOf(0L), result.first()); + assertEquals(Long.valueOf(0L), result.second()); + assertEquals(Long.valueOf(0L), result.third()); + } + @Test + public void testFindByHostIdType() { + CapacityVO capacity = new CapacityVO(); + capacity.setHostId(1L); + capacity.setCapacityType((short) 1); + + doReturn(capacity).when(capacityDao).findOneBy(any()); + + CapacityVO found = capacityDao.findByHostIdType(1L, (short) 1); + assertNotNull(found); + assertEquals(Long.valueOf(1L), found.getHostOrPoolId()); + } + + @Test + public void testUpdateAllocatedAddition() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + doNothing().when(txn).start(); + when(txn.commit()).thenReturn(true); + + capacityDao.updateAllocated(1L, 1000L, (short)1, true); + + verify(txn, times(1)).start(); + verify(txn, times(1)).commit(); + verify(pstmt, times(1)).executeUpdate(); + } + + @Test + public void testUpdateAllocatedSubtraction() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + doNothing().when(txn).start(); + when(txn.commit()).thenReturn(true); + + capacityDao.updateAllocated(1L, 500L, (short)1, false); + + verify(txn, times(1)).start(); + verify(txn, times(1)).commit(); + verify(pstmt, times(1)).executeUpdate(); + } + + @Test + public void testFindFilteredCapacityByEmptyResult() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + List result = capacityDao.findFilteredCapacityBy(null, null, null, null, Collections.emptyList(), Collections.emptyList()); + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + @Test + public void testListClustersInZoneOrPodByHostCapacitiesEmpty() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + + List resultZone = capacityDao.listClustersInZoneOrPodByHostCapacities(1L, 123L, 2, 2048L, true); + assertNotNull(resultZone); + assertTrue(resultZone.isEmpty()); + + List resultPod = capacityDao.listClustersInZoneOrPodByHostCapacities(1L, 123L, 2, 2048L, false); + assertNotNull(resultPod); + assertTrue(resultPod.isEmpty()); + } + + + @Test + public void testListHostsWithEnoughCapacityEmptyResult() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + + List result = capacityDao.listHostsWithEnoughCapacity(1, 100L, 200L, Host.Type.Routing.toString()); + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + + @Test + public void testOrderClustersByAggregateCapacityEmptyResult() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + + Pair, Map> result = capacityDao.orderClustersByAggregateCapacity(1L, 1L, (short) 1, true); + assertNotNull(result); + assertTrue(result.first().isEmpty()); + assertTrue(result.second().isEmpty()); + } + + + @Test + public void testOrderPodsByAggregateCapacityEmptyResult() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + + Pair, Map> result = capacityDao.orderPodsByAggregateCapacity(1L, (short) 1); + assertNotNull(result); + assertTrue(result.first().isEmpty()); + assertTrue(result.second().isEmpty()); + } + + + @Test + public void testUpdateCapacityState() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeUpdate()).thenReturn(1); + + capacityDao.updateCapacityState(1L, 1L, 1L, 1L, "Enabled", new short[]{1}); + + verify(pstmt, times(1)).executeUpdate(); + } + + + @Test + public void testFindClusterConsumption() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(true); + when(resultSet.getFloat(1)).thenReturn(0.5f); + + float result = capacityDao.findClusterConsumption(1L, (short) 1, 1000L); + assertEquals(0.5f, result, 0.0f); + } + + @Test + public void testListPodsByHostCapacitiesEmptyResult() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + + List result = capacityDao.listPodsByHostCapacities(1L, 2, 1024L); + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + @Test + public void testOrderHostsByFreeCapacityEmptyResult() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + + Pair, Map> result = capacityDao.orderHostsByFreeCapacity(1L, 1L, (short) 0); + assertNotNull(result); + assertTrue(result.first().isEmpty()); + } + + @Test + public void testFindByClusterPodZoneEmptyResult() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + + List result = capacityDao.findByClusterPodZone(1L, 1L, 1L); + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + @Test + public void testListCapacitiesGroupedByLevelAndTypeEmptyResult() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + + List result = capacityDao.listCapacitiesGroupedByLevelAndType(0, 1L, + 1L, 1L, 0, Collections.emptyList(), Collections.emptyList(), 1L); + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + @Test + public void testFindCapacityByEmptyResult() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + + List result = capacityDao.findCapacityBy(1, 1L, 1L, 1L); + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + @Test + public void testListHostCapacityByCapacityTypes_WithAllParameters() { + // Given + Long zoneId = 100L; + Long clusterId = 200L; + + // When + List result = capacityDao.listHostCapacityByCapacityTypes(zoneId, clusterId, capacityTypes); + + // Then + verify(searchBuilder).and("zoneId", mockEntity.getDataCenterId(), SearchCriteria.Op.EQ); + verify(searchBuilder).and("clusterId", mockEntity.getClusterId(), SearchCriteria.Op.EQ); + verify(searchBuilder).and("capacityTypes", mockEntity.getCapacityType(), SearchCriteria.Op.IN); + verify(searchBuilder).and("capacityState", mockEntity.getCapacityState(), SearchCriteria.Op.EQ); + + verify(searchCriteria).setParameters("capacityState", "Enabled"); + verify(searchCriteria).setParameters("zoneId", zoneId); + verify(searchCriteria).setParameters("clusterId", clusterId); + verify(searchCriteria).setParameters("capacityTypes", capacityTypes.toArray()); + + verify(capacityDao).listBy(searchCriteria); + assertEquals("Should return expected capacities", expectedCapacities, result); + } + + @Test + public void testListHostCapacityByCapacityTypes_WithNullZoneId() { + // Given + Long clusterId = 200L; + + // When + List result = capacityDao.listHostCapacityByCapacityTypes(null, clusterId, capacityTypes); + + // Then + verify(searchCriteria).setParameters("capacityState", "Enabled"); + verify(searchCriteria, Mockito.times(0)).setParameters(eq("zoneId"), any()); + verify(searchCriteria).setParameters("clusterId", clusterId); + verify(searchCriteria).setParameters("capacityTypes", capacityTypes.toArray()); + + assertEquals("Should return expected capacities", expectedCapacities, result); + } + + @Test + public void testListHostCapacityByCapacityTypes_WithNullClusterId() { + // Given + Long zoneId = 100L; + + // When + List result = capacityDao.listHostCapacityByCapacityTypes(zoneId, null, capacityTypes); + + // Then + verify(searchCriteria).setParameters("capacityState", "Enabled"); + verify(searchCriteria).setParameters("zoneId", zoneId); + verify(searchCriteria, never()).setParameters(eq("clusterId"), any()); + verify(searchCriteria).setParameters("capacityTypes", capacityTypes.toArray()); + + assertEquals("Should return expected capacities", expectedCapacities, result); + } + + @Test + public void testListHostCapacityByCapacityTypes_WithEmptyCapacityTypes() { + // Given + Long zoneId = 100L; + Long clusterId = 200L; + List emptyCapacityTypes = Collections.emptyList(); + + // When + List result = capacityDao.listHostCapacityByCapacityTypes(zoneId, clusterId, emptyCapacityTypes); + + // Then + verify(searchCriteria).setParameters("capacityTypes", emptyCapacityTypes.toArray()); + assertEquals("Should return expected capacities", expectedCapacities, result); + } + + @Test + public void testListPodCapacityByCapacityTypes_WithAllParameters() { + // Given + Long zoneId = 100L; + + // When + List result = capacityDao.listPodCapacityByCapacityTypes(zoneId, capacityTypes); + + // Then + verify(searchBuilder).and("zoneId", mockEntity.getDataCenterId(), SearchCriteria.Op.EQ); + verify(searchBuilder).and("capacityTypes", mockEntity.getCapacityType(), SearchCriteria.Op.IN); + verify(searchBuilder).and("capacityState", mockEntity.getCapacityState(), SearchCriteria.Op.EQ); + + verify(searchCriteria).setParameters("capacityState", "Enabled"); + verify(searchCriteria).setParameters("zoneId", zoneId); + verify(searchCriteria).setParameters("capacityTypes", capacityTypes.toArray()); + + assertEquals("Should return expected capacities", expectedCapacities, result); + } + + @Test + public void testListPodCapacityByCapacityTypes_WithNullZoneId() { + // When + List result = capacityDao.listPodCapacityByCapacityTypes(null, capacityTypes); + + // Then + verify(searchCriteria).setParameters("capacityState", "Enabled"); + verify(searchCriteria, never()).setParameters(eq("zoneId"), any()); + verify(searchCriteria).setParameters("capacityTypes", capacityTypes.toArray()); + + assertEquals("Should return expected capacities", expectedCapacities, result); + } + + @Test + public void testListClusterCapacityByCapacityTypes_WithAllParameters() { + // Given + Long zoneId = 100L; + Long podId = 300L; + + // When + List result = capacityDao.listClusterCapacityByCapacityTypes(zoneId, podId, capacityTypes); + + // Then + verify(searchBuilder).and("zoneId", mockEntity.getDataCenterId(), SearchCriteria.Op.EQ); + verify(searchBuilder).and("podId", mockEntity.getPodId(), SearchCriteria.Op.EQ); + verify(searchBuilder).and("capacityTypes", mockEntity.getCapacityType(), SearchCriteria.Op.IN); + verify(searchBuilder).and("capacityState", mockEntity.getCapacityState(), SearchCriteria.Op.EQ); + + verify(searchCriteria).setParameters("capacityState", "Enabled"); + verify(searchCriteria).setParameters("zoneId", zoneId); + verify(searchCriteria).setParameters("podId", podId); + verify(searchCriteria).setParameters("capacityTypes", capacityTypes.toArray()); + + assertEquals("Should return expected capacities", expectedCapacities, result); + } + + @Test + public void testListClusterCapacityByCapacityTypes_WithNullZoneId() { + // Given + Long podId = 300L; + + // When + List result = capacityDao.listClusterCapacityByCapacityTypes(null, podId, capacityTypes); + + // Then + verify(searchCriteria).setParameters("capacityState", "Enabled"); + verify(searchCriteria, never()).setParameters(eq("zoneId"), any()); + verify(searchCriteria).setParameters("podId", podId); + verify(searchCriteria).setParameters("capacityTypes", capacityTypes.toArray()); + + assertEquals("Should return expected capacities", expectedCapacities, result); + } + + @Test + public void testListClusterCapacityByCapacityTypes_WithNullPodId() { + // Given + Long zoneId = 100L; + + // When + List result = capacityDao.listClusterCapacityByCapacityTypes(zoneId, null, capacityTypes); + + // Then + verify(searchCriteria).setParameters("capacityState", "Enabled"); + verify(searchCriteria).setParameters("zoneId", zoneId); + verify(searchCriteria, never()).setParameters(eq("podId"), any()); + verify(searchCriteria).setParameters("capacityTypes", capacityTypes.toArray()); + + assertEquals("Should return expected capacities", expectedCapacities, result); + } + + @Test + public void testListClusterCapacityByCapacityTypes_WithBothIdsNull() { + // When + List result = capacityDao.listClusterCapacityByCapacityTypes(null, null, capacityTypes); + + // Then + verify(searchCriteria).setParameters("capacityState", "Enabled"); + verify(searchCriteria, never()).setParameters(eq("zoneId"), any()); + verify(searchCriteria, never()).setParameters(eq("podId"), any()); + verify(searchCriteria).setParameters("capacityTypes", capacityTypes.toArray()); + + assertEquals("Should return expected capacities", expectedCapacities, result); + } + + @Test + public void testAllMethods_VerifySearchBuilderSetup() { + // Test that all methods properly set up the search builder + Long zoneId = 100L; + Long clusterId = 200L; + Long podId = 300L; + + // Test host capacity method + capacityDao.listHostCapacityByCapacityTypes(zoneId, clusterId, capacityTypes); + + // Test pod capacity method + capacityDao.listPodCapacityByCapacityTypes(zoneId, capacityTypes); + + // Test cluster capacity method + capacityDao.listClusterCapacityByCapacityTypes(zoneId, podId, capacityTypes); + + // Verify createSearchBuilder was called 3 times + verify(capacityDao, times(3)).createSearchBuilder(); + + // Verify done() was called 3 times + verify(searchBuilder, times(3)).done(); + + // Verify listBy was called 3 times + verify(capacityDao, times(3)).listBy(searchCriteria); + } +} diff --git a/engine/schema/src/test/java/com/cloud/dc/dao/ClusterDaoImplTest.java b/engine/schema/src/test/java/com/cloud/dc/dao/ClusterDaoImplTest.java new file mode 100644 index 000000000000..c1a558a72b33 --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/dc/dao/ClusterDaoImplTest.java @@ -0,0 +1,118 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES 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.dc.dao; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import com.cloud.cpu.CPU; +import com.cloud.dc.ClusterVO; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.utils.Pair; +import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@RunWith(MockitoJUnitRunner.class) +public class ClusterDaoImplTest { + @Spy + @InjectMocks + ClusterDaoImpl clusterDao = new ClusterDaoImpl(); + + private GenericSearchBuilder genericSearchBuilder; + + @Before + public void setUp() { + genericSearchBuilder = mock(SearchBuilder.class); + ClusterVO entityVO = mock(ClusterVO.class); + when(genericSearchBuilder.entity()).thenReturn(entityVO); + doReturn(genericSearchBuilder).when(clusterDao).createSearchBuilder(Long.class); + } + + @Test + public void testListAllIds() { + List mockIds = Arrays.asList(1L, 2L, 3L); + doReturn(mockIds).when(clusterDao).customSearch(any(), isNull()); + List result = clusterDao.listAllIds(); + verify(clusterDao).customSearch(genericSearchBuilder.create(), null); + assertEquals(3, result.size()); + assertEquals(Long.valueOf(1L), result.get(0)); + assertEquals(Long.valueOf(2L), result.get(1)); + assertEquals(Long.valueOf(3L), result.get(2)); + } + + @Test + public void testListAllIdsEmptyResult() { + doReturn(Collections.emptyList()).when(clusterDao).customSearch(any(), isNull()); + List result = clusterDao.listAllIds(); + verify(clusterDao).customSearch(genericSearchBuilder.create(), null); + assertTrue(result.isEmpty()); + } + + @Test + public void listDistinctHypervisorsArchAcrossClusters_WithZone() { + Long zoneId = 123L; + ClusterVO cluster1 = mock(ClusterVO.class); + when(cluster1.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.XenServer); + when(cluster1.getArch()).thenReturn(CPU.CPUArch.amd64); + ClusterVO cluster2 = mock(ClusterVO.class); + when(cluster2.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); + when(cluster2.getArch()).thenReturn(CPU.CPUArch.arm64); + List dummyHosts = Arrays.asList(cluster1, cluster2); + doReturn(dummyHosts).when(clusterDao).search(any(SearchCriteria.class), isNull()); + List> result = clusterDao.listDistinctHypervisorsAndArchExcludingExternalType(zoneId); + assertNotNull(result); + assertEquals(2, result.size()); + assertEquals(Hypervisor.HypervisorType.XenServer, result.get(0).first()); + assertEquals(CPU.CPUArch.amd64, result.get(0).second()); + assertEquals(Hypervisor.HypervisorType.KVM, result.get(1).first()); + assertEquals(CPU.CPUArch.arm64, result.get(1).second()); + } + + @Test + public void listDistinctHypervisorsArchAcrossClusters_WithoutZone() { + Long zoneId = null; + ClusterVO cluster = mock(ClusterVO.class); + when(cluster.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.VMware); + when(cluster.getArch()).thenReturn(CPU.CPUArch.amd64); + List dummyHosts = Collections.singletonList(cluster); + doReturn(dummyHosts).when(clusterDao).search(any(SearchCriteria.class), isNull()); + List> result = clusterDao.listDistinctHypervisorsAndArchExcludingExternalType(zoneId); + assertNotNull(result); + assertEquals(1, result.size()); + assertEquals(Hypervisor.HypervisorType.VMware, result.get(0).first()); + assertEquals(CPU.CPUArch.amd64, result.get(0).second()); + } +} diff --git a/engine/schema/src/test/java/com/cloud/gpu/dao/GpuCardDaoImplTest.java b/engine/schema/src/test/java/com/cloud/gpu/dao/GpuCardDaoImplTest.java new file mode 100644 index 000000000000..e0a283add992 --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/gpu/dao/GpuCardDaoImplTest.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.cloud.gpu.dao; + +import com.cloud.gpu.GpuCardVO; +import com.cloud.utils.db.SearchCriteria; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +@RunWith(MockitoJUnitRunner.class) +public class GpuCardDaoImplTest { + + @Spy + @InjectMocks + GpuCardDaoImpl gpuCardDaoImpl = new GpuCardDaoImpl(); + + @Test + public void findByVendorIdAndDeviceId() { + doReturn(mock(GpuCardVO.class)).when(gpuCardDaoImpl).findOneBy(any(SearchCriteria.class)); + + GpuCardVO gpuCard = gpuCardDaoImpl.findByVendorIdAndDeviceId("0d1a", "1a3b"); + Assert.assertNotNull("Expected non-null gpu card", gpuCard); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuCardDaoImpl).findOneBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", + "gpu_card.vendor_id = ? AND gpu_card.device_id = ?", + scCaptor.getValue().getWhereClause().trim()); + } +} diff --git a/engine/schema/src/test/java/com/cloud/gpu/dao/GpuDeviceDaoImplTest.java b/engine/schema/src/test/java/com/cloud/gpu/dao/GpuDeviceDaoImplTest.java new file mode 100644 index 000000000000..1780fbd3df38 --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/gpu/dao/GpuDeviceDaoImplTest.java @@ -0,0 +1,277 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.cloud.gpu.dao; + +import com.cloud.gpu.GpuDeviceVO; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.SearchCriteria; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Collections; +import java.util.List; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class GpuDeviceDaoImplTest { + + @Spy + @InjectMocks + GpuDeviceDaoImpl gpuDeviceDao = new GpuDeviceDaoImpl(); + + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void listByIds_emptyList() { + List devices = gpuDeviceDao.listByIds(null); + Assert.assertTrue("Expected empty list", devices.isEmpty()); + devices = gpuDeviceDao.listByIds(Collections.emptyList()); + Assert.assertTrue("Expected empty list", devices.isEmpty()); + } + + @Test + public void listByIds() { + doReturn(List.of(mock(GpuDeviceVO.class))).when(gpuDeviceDao).listBy(any(SearchCriteria.class)); + + List devices = gpuDeviceDao.listByIds(List.of(1L, 2L, 3L)); + + Assert.assertFalse("Expected non empty list", devices.isEmpty()); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).listBy(scCaptor.capture()); + SearchCriteria sc = scCaptor.getValue(); + Assert.assertEquals("Expected correct where clause", "gpu_device.id IN (?,?,?)", sc.getWhereClause().trim()); + } + + @Test + public void findByHostIdAndBusAddress() { + doReturn(mock(GpuDeviceVO.class)).when(gpuDeviceDao).findOneBy(any(SearchCriteria.class)); + + GpuDeviceVO device = gpuDeviceDao.findByHostIdAndBusAddress(1L, "0000:00:1f.6"); + + Assert.assertNotNull("Expected non-null device", device); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).findOneBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "gpu_device.host_id = ? AND gpu_device.bus_address = ?", + scCaptor.getValue().getWhereClause().trim()); + } + + @Test + public void listByHostId() { + doReturn(List.of(mock(GpuDeviceVO.class))).when(gpuDeviceDao).listBy(any(SearchCriteria.class)); + + List devices = gpuDeviceDao.listByHostId(1L); + + Assert.assertFalse("Expected non empty list", devices.isEmpty()); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).listBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "gpu_device.host_id = ?", + scCaptor.getValue().getWhereClause().trim()); + } + + @Test + public void listByVmId() { + doReturn(List.of(mock(GpuDeviceVO.class))).when(gpuDeviceDao).listBy(any(SearchCriteria.class)); + + List devices = gpuDeviceDao.listByVmId(1L); + + Assert.assertFalse("Expected non empty list", devices.isEmpty()); + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).listBy(scCaptor.capture()); + + Assert.assertEquals("Expected correct where clause", "gpu_device.vm_id = ?", + scCaptor.getValue().getWhereClause().trim()); + } + + @Test + public void isVgpuProfileInUse() { + doReturn(1).when(gpuDeviceDao).getCount(any(SearchCriteria.class)); + + boolean vgpuProfileInUse = gpuDeviceDao.isVgpuProfileInUse(1L); + + Assert.assertTrue("Expected vGPU profile to be in use", vgpuProfileInUse); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).getCount(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "gpu_device.vgpu_profile_id = ?", + scCaptor.getValue().getWhereClause().trim()); + } + + @Test + public void isGpuCardInUse() { + doReturn(1).when(gpuDeviceDao).getCount(any(SearchCriteria.class)); + + boolean vgpuProfileInUse = gpuDeviceDao.isGpuCardInUse(1L); + + Assert.assertTrue("Expected GPU Card to be in use", vgpuProfileInUse); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).getCount(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "gpu_device.card_id = ?", + scCaptor.getValue().getWhereClause().trim()); + } + + @Test + public void listByHostAndVm() { + doReturn(List.of(mock(GpuDeviceVO.class))).when(gpuDeviceDao).search(any(SearchCriteria.class), any()); + + List devices = gpuDeviceDao.listByHostAndVm(1L, 2L); + + Assert.assertFalse("Expected non empty list", devices.isEmpty()); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + ArgumentCaptor filterCaptor = ArgumentCaptor.forClass(Filter.class); + verify(gpuDeviceDao).search(scCaptor.capture(), filterCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "gpu_device.host_id = ? AND gpu_device.vm_id = ?", + scCaptor.getValue().getWhereClause().trim()); + Assert.assertNull("Expected no filter", filterCaptor.getValue()); + } + + @Test + public void listDevicesForAllocation() { + doReturn(List.of(mock(GpuDeviceVO.class))).when(gpuDeviceDao).search(any(SearchCriteria.class), any()); + + List devices = gpuDeviceDao.listDevicesForAllocation(1L, 2L); + + Assert.assertFalse("Expected non empty list", devices.isEmpty()); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + ArgumentCaptor filterCaptor = ArgumentCaptor.forClass(Filter.class); + verify(gpuDeviceDao).search(scCaptor.capture(), filterCaptor.capture()); + Assert.assertEquals("Expected correct where clause", + "gpu_device.host_id = ? AND gpu_device.vgpu_profile_id=? AND gpu_device.state = ? AND gpu_device" + + ".managed_state = ? AND gpu_device.type != ?", + scCaptor.getValue().getWhereClause().trim()); + Assert.assertNull("Expected no filter", filterCaptor.getValue()); + } + + @Test + public void searchAndCountGpuDevices() { + } + + @Test + public void getDistinctGpuCardIds_no_devices() { + doReturn(null).when(gpuDeviceDao).listBy(any(SearchCriteria.class)); + + List cardIds = gpuDeviceDao.getDistinctGpuCardIds(); + + Assert.assertTrue("Expected empty list", cardIds.isEmpty()); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).listBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "", scCaptor.getValue().getWhereClause().trim()); + } + + + @Test + public void getDistinctGpuCardIds() { + GpuDeviceVO device1 = mock(GpuDeviceVO.class); + GpuDeviceVO device2 = mock(GpuDeviceVO.class); + GpuDeviceVO device3 = mock(GpuDeviceVO.class); + when(device1.getCardId()).thenReturn(1L); + when(device2.getCardId()).thenReturn(2L); + when(device3.getCardId()).thenReturn(1L); + + doReturn(List.of(device1, device2, device3)).when(gpuDeviceDao).listBy(any(SearchCriteria.class)); + + List cardIds = gpuDeviceDao.getDistinctGpuCardIds(); + + Assert.assertEquals("Expected 2 card IDs", 2, cardIds.size()); + + Assert.assertTrue("Expected card ID 1 in list", cardIds.contains(1L)); + Assert.assertTrue("Expected card ID 2 in list", cardIds.contains(2L)); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).listBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "", scCaptor.getValue().getWhereClause().trim()); + } + + @Test + public void getDistinctVgpuProfileIds_no_devices() { + doReturn(null).when(gpuDeviceDao).listBy(any(SearchCriteria.class)); + + List cardIds = gpuDeviceDao.getDistinctVgpuProfileIds(); + + Assert.assertTrue("Expected empty list", cardIds.isEmpty()); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).listBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "", scCaptor.getValue().getWhereClause().trim()); + } + + + @Test + public void getDistinctVgpuProfileIds() { + GpuDeviceVO device1 = mock(GpuDeviceVO.class); + GpuDeviceVO device2 = mock(GpuDeviceVO.class); + GpuDeviceVO device3 = mock(GpuDeviceVO.class); + when(device1.getVgpuProfileId()).thenReturn(1L); + when(device2.getVgpuProfileId()).thenReturn(2L); + when(device3.getVgpuProfileId()).thenReturn(1L); + + doReturn(List.of(device1, device2, device3)).when(gpuDeviceDao).listBy(any(SearchCriteria.class)); + + List cardIds = gpuDeviceDao.getDistinctVgpuProfileIds(); + + Assert.assertEquals("Expected 2 VgpuProfile IDs", 2, cardIds.size()); + + Assert.assertTrue("Expected VgpuProfile ID 1 in list", cardIds.contains(1L)); + Assert.assertTrue("Expected VgpuProfile ID 2 in list", cardIds.contains(2L)); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).listBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "", scCaptor.getValue().getWhereClause().trim()); + } + + + @Test + public void listByParentGpuDeviceId() { + doReturn(List.of(mock(GpuDeviceVO.class))).when(gpuDeviceDao).listBy(any(SearchCriteria.class)); + + List devices = gpuDeviceDao.listByParentGpuDeviceId(1L); + + Assert.assertFalse("Expected non empty list", devices.isEmpty()); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).listBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "gpu_device.parent_gpu_device_id = ?", + scCaptor.getValue().getWhereClause().trim()); + } +} diff --git a/engine/schema/src/test/java/com/cloud/gpu/dao/VgpuProfileDaoImplTest.java b/engine/schema/src/test/java/com/cloud/gpu/dao/VgpuProfileDaoImplTest.java new file mode 100644 index 000000000000..cd7199d020f3 --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/gpu/dao/VgpuProfileDaoImplTest.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.cloud.gpu.dao; + +import com.cloud.gpu.VgpuProfileVO; +import com.cloud.utils.db.SearchCriteria; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +@RunWith(MockitoJUnitRunner.class) +public class VgpuProfileDaoImplTest { + + @Spy + @InjectMocks + VgpuProfileDaoImpl vgpuProfileDaoImpl = new VgpuProfileDaoImpl(); + + @Test + public void findByNameAndCardId() { + doReturn(mock(VgpuProfileVO.class)).when(vgpuProfileDaoImpl).findOneBy(any(SearchCriteria.class)); + + VgpuProfileVO vgpuProfile = vgpuProfileDaoImpl.findByNameAndCardId("test-profile", 1L); + Assert.assertNotNull("Expected non-null vgpu profile", vgpuProfile); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(vgpuProfileDaoImpl).findOneBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", + "vgpu_profile.name = ? AND vgpu_profile.card_id=?", + scCaptor.getValue().getWhereClause().trim()); + } + + @Test + public void removeByCardId() { + doReturn(1).when(vgpuProfileDaoImpl).remove(any(SearchCriteria.class)); + + int removed = vgpuProfileDaoImpl.removeByCardId(123L); + Assert.assertEquals("Expected one vgpu profile removed", 1, removed); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(vgpuProfileDaoImpl).remove(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "vgpu_profile.card_id=?", + scCaptor.getValue().getWhereClause().trim()); + } +} diff --git a/engine/schema/src/test/java/com/cloud/host/HostVOTest.java b/engine/schema/src/test/java/com/cloud/host/HostVOTest.java index 76bc5270b4f7..3262c4cc2918 100755 --- a/engine/schema/src/test/java/com/cloud/host/HostVOTest.java +++ b/engine/schema/src/test/java/com/cloud/host/HostVOTest.java @@ -1,84 +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 com.cloud.host; - -import com.cloud.service.ServiceOfferingVO; -import com.cloud.vm.VirtualMachine; -import java.util.Arrays; -import java.util.List; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import org.junit.Test; -import org.junit.Before; - -public class HostVOTest { - HostVO host; - ServiceOfferingVO offering; - - @Before - public void setUp() throws Exception { - host = new HostVO(); - offering = new ServiceOfferingVO("TestSO", 0, 0, 0, 0, 0, - false, "TestSO", false,VirtualMachine.Type.User,false); - } - - @Test - public void testNoSO() { - assertFalse(host.checkHostServiceOfferingTags(null)); - } - - @Test - public void testNoTag() { - assertTrue(host.checkHostServiceOfferingTags(offering)); - } - - @Test - public void testRightTag() { - host.setHostTags(Arrays.asList("tag1","tag2"), false); - offering.setHostTag("tag2,tag1"); - assertTrue(host.checkHostServiceOfferingTags(offering)); - } - - @Test - public void testWrongTag() { - host.setHostTags(Arrays.asList("tag1","tag2"), false); - offering.setHostTag("tag2,tag4"); - assertFalse(host.checkHostServiceOfferingTags(offering)); - } - - @Test - public void checkHostServiceOfferingTagsTestRuleTagWithServiceTagThatMatches() { - host.setHostTags(List.of("tags[0] == 'A'"), true); - offering.setHostTag("A"); - assertTrue(host.checkHostServiceOfferingTags(offering)); - } - - @Test - public void checkHostServiceOfferingTagsTestRuleTagWithServiceTagThatDoesNotMatch() { - host.setHostTags(List.of("tags[0] == 'A'"), true); - offering.setHostTag("B"); - assertFalse(host.checkHostServiceOfferingTags(offering)); - } - - @Test - public void checkHostServiceOfferingTagsTestRuleTagWithNullServiceTag() { - host.setHostTags(List.of("tags[0] == 'A'"), true); - offering.setHostTag(null); - assertFalse(host.checkHostServiceOfferingTags(offering)); - } -} +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES 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.host; + +import com.cloud.offering.ServiceOffering; +import com.cloud.service.ServiceOfferingVO; +import com.cloud.template.VirtualMachineTemplate; +import com.cloud.vm.VirtualMachine; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class HostVOTest { + HostVO host; + ServiceOfferingVO offering; + + @Before + public void setUp() throws Exception { + host = new HostVO(); + offering = new ServiceOfferingVO("TestSO", 0, 0, 0, 0, 0, + false, "TestSO", false, VirtualMachine.Type.User, false); + } + + @Test + public void testNoSO() { + assertFalse(host.checkHostServiceOfferingTags(null)); + } + + @Test + public void testNoTag() { + assertTrue(host.checkHostServiceOfferingTags(offering)); + } + + @Test + public void testRightTag() { + host.setHostTags(Arrays.asList("tag1", "tag2"), false); + offering.setHostTag("tag2,tag1"); + assertTrue(host.checkHostServiceOfferingTags(offering)); + } + + @Test + public void testWrongTag() { + host.setHostTags(Arrays.asList("tag1", "tag2"), false); + offering.setHostTag("tag2,tag4"); + assertFalse(host.checkHostServiceOfferingTags(offering)); + } + + @Test + public void checkHostServiceOfferingTagsTestRuleTagWithServiceTagThatMatches() { + host.setHostTags(List.of("tags[0] == 'A'"), true); + offering.setHostTag("A"); + assertTrue(host.checkHostServiceOfferingTags(offering)); + } + + @Test + public void checkHostServiceOfferingTagsTestRuleTagWithServiceTagThatDoesNotMatch() { + host.setHostTags(List.of("tags[0] == 'A'"), true); + offering.setHostTag("B"); + assertFalse(host.checkHostServiceOfferingTags(offering)); + } + + @Test + public void checkHostServiceOfferingTagsTestRuleTagWithNullServiceTag() { + host.setHostTags(List.of("tags[0] == 'A'"), true); + offering.setHostTag(null); + assertFalse(host.checkHostServiceOfferingTags(offering)); + } + + @Test + public void testEitherNoSOOrTemplate() { + assertFalse(host.checkHostServiceOfferingAndTemplateTags(null, Mockito.mock(VirtualMachineTemplate.class), null)); + assertFalse(host.checkHostServiceOfferingAndTemplateTags(Mockito.mock(ServiceOffering.class), null, null)); + } + + @Test + public void testNoTagOfferingTemplate() { + assertTrue(host.checkHostServiceOfferingAndTemplateTags(offering, Mockito.mock(VirtualMachineTemplate.class), Collections.emptySet())); + assertTrue(host.getHostServiceOfferingAndTemplateMissingTags(offering, Mockito.mock(VirtualMachineTemplate.class), Collections.emptySet()).isEmpty()); + assertTrue(host.checkHostServiceOfferingAndTemplateTags(offering, Mockito.mock(VirtualMachineTemplate.class), Set.of("tag1", "tag2"))); + assertTrue(host.getHostServiceOfferingAndTemplateMissingTags(offering, Mockito.mock(VirtualMachineTemplate.class), Set.of("tag1", "tag2")).isEmpty()); + } + + @Test + public void testRightTagOfferingTemplate() { + host.setHostTags(Arrays.asList("tag1", "tag2"), false); + offering.setHostTag("tag2,tag1"); + assertTrue(host.checkHostServiceOfferingAndTemplateTags(offering, Mockito.mock(VirtualMachineTemplate.class), Set.of("tag1"))); + Set actualMissingTags = host.getHostServiceOfferingAndTemplateMissingTags(offering, Mockito.mock(VirtualMachineTemplate.class), Set.of("tag1")); + assertTrue(actualMissingTags.isEmpty()); + + host.setHostTags(Arrays.asList("tag1", "tag2", "tag3"), false); + offering.setHostTag("tag2,tag1"); + VirtualMachineTemplate template = Mockito.mock(VirtualMachineTemplate.class); + Mockito.when(template.getTemplateTag()).thenReturn("tag3"); + assertTrue(host.checkHostServiceOfferingAndTemplateTags(offering, template, Set.of("tag2", "tag3"))); + actualMissingTags = host.getHostServiceOfferingAndTemplateMissingTags(offering, template, Set.of("tag2", "tag3")); + assertTrue(actualMissingTags.isEmpty()); + host.setHostTags(List.of("tag3"), false); + offering.setHostTag(null); + assertTrue(host.checkHostServiceOfferingAndTemplateTags(offering, template, Set.of("tag3"))); + actualMissingTags = host.getHostServiceOfferingAndTemplateMissingTags(offering, template, Set.of("tag3")); + assertTrue(actualMissingTags.isEmpty()); + + assertTrue(host.checkHostServiceOfferingAndTemplateTags(offering, template, Set.of("tag2", "tag1"))); + actualMissingTags = host.getHostServiceOfferingAndTemplateMissingTags(offering, template, Set.of("tag2", "tag1")); + assertTrue(actualMissingTags.isEmpty()); + } + + @Test + public void testWrongOfferingTag() { + host.setHostTags(Arrays.asList("tag1", "tag2"), false); + offering.setHostTag("tag2,tag4"); + VirtualMachineTemplate template = Mockito.mock(VirtualMachineTemplate.class); + Mockito.when(template.getTemplateTag()).thenReturn("tag1"); + assertFalse(host.checkHostServiceOfferingAndTemplateTags(offering, template, Set.of("tag1", "tag2", "tag3", "tag4"))); + Set actualMissingTags = host.getHostServiceOfferingAndTemplateMissingTags(offering, template, Set.of("tag1", "tag2", "tag3", "tag4")); + assertEquals(Set.of("tag4"), actualMissingTags); + + offering.setHostTag("tag1,tag2"); + template = Mockito.mock(VirtualMachineTemplate.class); + Mockito.when(template.getTemplateTag()).thenReturn("tag3"); + actualMissingTags = host.getHostServiceOfferingAndTemplateMissingTags(offering, template, Set.of("tag1", "tag2", "tag3", "tag4")); + assertFalse(host.checkHostServiceOfferingAndTemplateTags(offering, template, Set.of("tag1", "tag2", "tag3", "tag4"))); + assertEquals(Set.of("tag3"), actualMissingTags); + } +} diff --git a/engine/schema/src/test/java/com/cloud/host/dao/HostDaoImplTest.java b/engine/schema/src/test/java/com/cloud/host/dao/HostDaoImplTest.java new file mode 100644 index 000000000000..c76eedb01e93 --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/host/dao/HostDaoImplTest.java @@ -0,0 +1,252 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES 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.host.dao; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import com.cloud.cpu.CPU; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.Status; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.resource.ResourceState; +import com.cloud.utils.Pair; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@RunWith(MockitoJUnitRunner.class) +public class HostDaoImplTest { + + @Spy + HostDaoImpl hostDao = new HostDaoImpl(); + + @Mock + private SearchBuilder mockSearchBuilder; + @Mock + private SearchCriteria mockSearchCriteria; + + @Test + public void testCountUpAndEnabledHostsInZone() { + long testZoneId = 100L; + hostDao.HostTypeCountSearch = mockSearchBuilder; + when(mockSearchBuilder.create()).thenReturn(mockSearchCriteria); + Mockito.doNothing().when(mockSearchCriteria).setParameters(Mockito.anyString(), any()); + int expected = 5; + doReturn(expected).when(hostDao).getCount(mockSearchCriteria); + Integer count = hostDao.countUpAndEnabledHostsInZone(testZoneId); + Assert.assertSame(expected, count); + Mockito.verify(mockSearchCriteria).setParameters("type", Host.Type.Routing); + Mockito.verify(mockSearchCriteria).setParameters("resourceState", ResourceState.Enabled); + Mockito.verify(mockSearchCriteria).setParameters("zoneId", testZoneId); + Mockito.verify(hostDao).getCount(mockSearchCriteria); + } + + @Test + public void testCountAllHostsAndCPUSocketsByType() { + Host.Type type = Host.Type.Routing; + GenericDaoBase.SumCount mockSumCount = new GenericDaoBase.SumCount(); + mockSumCount.count = 10; + mockSumCount.sum = 20; + HostVO host = mock(HostVO.class); + GenericSearchBuilder sb = mock(GenericSearchBuilder.class); + when(sb.entity()).thenReturn(host); + doReturn(sb).when(hostDao).createSearchBuilder(GenericDaoBase.SumCount.class); + SearchCriteria sc = mock(SearchCriteria.class); + when(sb.create()).thenReturn(sc); + doReturn(List.of(mockSumCount)).when(hostDao).customSearch(any(SearchCriteria.class), any()); + Pair result = hostDao.countAllHostsAndCPUSocketsByType(type); + assertEquals(10, result.first().intValue()); + assertEquals(20, result.second().intValue()); + Mockito.verify(sc).setParameters("type", type); + } + + @Test + public void testIsHostUp() { + long testHostId = 101L; + List statuses = List.of(Status.Up); + HostVO host = mock(HostVO.class); + GenericSearchBuilder sb = mock(GenericSearchBuilder.class); + when(sb.entity()).thenReturn(host); + SearchCriteria sc = mock(SearchCriteria.class); + when(sb.create()).thenReturn(sc); + doReturn(sb).when(hostDao).createSearchBuilder(Status.class); + doReturn(statuses).when(hostDao).customSearch(any(SearchCriteria.class), any()); + boolean result = hostDao.isHostUp(testHostId); + Assert.assertTrue("Host should be up", result); + Mockito.verify(sc).setParameters("id", testHostId); + Mockito.verify(hostDao).customSearch(sc, null); + } + + @Test + public void testFindHostIdsByZoneClusterResourceStateTypeAndHypervisorType() { + Long zoneId = 1L; + Long clusterId = 2L; + Long msId = 1L; + List resourceStates = List.of(ResourceState.Enabled); + List types = List.of(Host.Type.Routing); + List hypervisorTypes = List.of(Hypervisor.HypervisorType.KVM); + List mockResults = List.of(1001L, 1002L); // Mocked result + HostVO host = mock(HostVO.class); + GenericSearchBuilder sb = mock(GenericSearchBuilder.class); + when(sb.entity()).thenReturn(host); + SearchCriteria sc = mock(SearchCriteria.class); + when(sb.create()).thenReturn(sc); + when(sb.and()).thenReturn(sb); + doReturn(sb).when(hostDao).createSearchBuilder(Long.class); + doReturn(mockResults).when(hostDao).customSearch(any(SearchCriteria.class), any()); + List hostIds = hostDao.findHostIdsByZoneClusterResourceStateTypeAndHypervisorType( + zoneId, clusterId, msId, resourceStates, types, hypervisorTypes); + assertEquals(mockResults, hostIds); + Mockito.verify(sc).setParameters("zoneId", zoneId); + Mockito.verify(sc).setParameters("clusterId", clusterId); + Mockito.verify(sc).setParameters("msId", msId); + Mockito.verify(sc).setParameters("resourceState", resourceStates.toArray()); + Mockito.verify(sc).setParameters("type", types.toArray()); + Mockito.verify(sc).setParameters("hypervisorTypes", hypervisorTypes.toArray()); + } + + @Test + public void testListDistinctHypervisorTypes() { + Long zoneId = 1L; + List mockResults = List.of(Hypervisor.HypervisorType.KVM, Hypervisor.HypervisorType.XenServer); + HostVO host = mock(HostVO.class); + GenericSearchBuilder sb = mock(GenericSearchBuilder.class); + when(sb.entity()).thenReturn(host); + SearchCriteria sc = mock(SearchCriteria.class); + when(sb.create()).thenReturn(sc); + doReturn(sb).when(hostDao).createSearchBuilder(String.class); + doReturn(mockResults.stream().map(h -> h.name()).collect(Collectors.toList())).when(hostDao) + .customSearch(any(SearchCriteria.class), any()); + List hypervisorTypes = hostDao.listDistinctHypervisorTypes(zoneId); + assertEquals(mockResults, hypervisorTypes); + Mockito.verify(sc).setParameters("zoneId", zoneId); + Mockito.verify(sc).setParameters("type", Host.Type.Routing); + } + + @Test + public void testListByIds() { + List ids = List.of(101L, 102L); + List mockResults = List.of(mock(HostVO.class), mock(HostVO.class)); + hostDao.IdsSearch = mockSearchBuilder; + when(mockSearchBuilder.create()).thenReturn(mockSearchCriteria); + doReturn(mockResults).when(hostDao).search(any(SearchCriteria.class), any()); + List hosts = hostDao.listByIds(ids); + assertEquals(mockResults, hosts); + Mockito.verify(mockSearchCriteria).setParameters("id", ids.toArray()); + Mockito.verify(hostDao).search(mockSearchCriteria, null); + } + + @Test + public void testListIdsBy() { + Host.Type type = Host.Type.Routing; + Status status = Status.Up; + ResourceState resourceState = ResourceState.Enabled; + Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM; + Long zoneId = 1L, podId = 2L, clusterId = 3L; + List mockResults = List.of(1001L, 1002L); + HostVO host = mock(HostVO.class); + GenericSearchBuilder sb = mock(GenericSearchBuilder.class); + when(sb.entity()).thenReturn(host); + SearchCriteria sc = mock(SearchCriteria.class); + when(sb.create()).thenReturn(sc); + doReturn(sb).when(hostDao).createSearchBuilder(Long.class); + doReturn(mockResults).when(hostDao).customSearch(any(SearchCriteria.class), any()); + List hostIds = hostDao.listIdsBy(type, status, resourceState, hypervisorType, zoneId, podId, clusterId); + assertEquals(mockResults, hostIds); + Mockito.verify(sc).setParameters("type", type); + Mockito.verify(sc).setParameters("status", status); + Mockito.verify(sc).setParameters("resourceState", resourceState); + Mockito.verify(sc).setParameters("hypervisorType", hypervisorType); + Mockito.verify(sc).setParameters("zoneId", zoneId); + Mockito.verify(sc).setParameters("podId", podId); + Mockito.verify(sc).setParameters("clusterId", clusterId); + } + + @Test + public void testListDistinctHypervisorArchTypes_WithZone() { + Long zoneId = 123L; + HostVO host1 = mock(HostVO.class); + when(host1.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.XenServer); + when(host1.getArch()).thenReturn(CPU.CPUArch.amd64); + HostVO host2 = mock(HostVO.class); + when(host2.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); + when(host2.getArch()).thenReturn(CPU.CPUArch.arm64); + List dummyHosts = Arrays.asList(host1, host2); + doReturn(dummyHosts).when(hostDao).search(any(SearchCriteria.class), isNull()); + List> result = hostDao.listDistinctHypervisorArchTypes(zoneId); + assertNotNull(result); + assertEquals(2, result.size()); + assertEquals(Hypervisor.HypervisorType.XenServer, result.get(0).first()); + assertEquals(CPU.CPUArch.amd64, result.get(0).second()); + assertEquals(Hypervisor.HypervisorType.KVM, result.get(1).first()); + assertEquals(CPU.CPUArch.arm64, result.get(1).second()); + } + + @Test + public void testListDistinctHypervisorArchTypes_WithoutZone() { + Long zoneId = null; + HostVO host1 = mock(HostVO.class); + when(host1.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.VMware); + when(host1.getArch()).thenReturn(CPU.CPUArch.amd64); + List dummyHosts = Collections.singletonList(host1); + doReturn(dummyHosts).when(hostDao).search(any(SearchCriteria.class), isNull()); + List> result = hostDao.listDistinctHypervisorArchTypes(zoneId); + assertNotNull(result); + assertEquals(1, result.size()); + assertEquals(Hypervisor.HypervisorType.VMware, result.get(0).first()); + assertEquals(CPU.CPUArch.amd64, result.get(0).second()); + } + + @Test + public void testListDistinctArchTypes() { + Long clusterId = 1L; + List mockResults = List.of(CPU.CPUArch.amd64, CPU.CPUArch.arm64); + HostVO host = mock(HostVO.class); + GenericSearchBuilder sb = mock(GenericSearchBuilder.class); + when(sb.entity()).thenReturn(host); + SearchCriteria sc = mock(SearchCriteria.class); + when(sb.create()).thenReturn(sc); + doReturn(sb).when(hostDao).createSearchBuilder(String.class); + doReturn(mockResults.stream().map(h -> h.getType()).collect(Collectors.toList())).when(hostDao) + .customSearch(any(SearchCriteria.class), any()); + List hypervisorTypes = hostDao.listDistinctArchTypes(clusterId); + assertEquals(mockResults, hypervisorTypes); + Mockito.verify(sc).setParameters("clusterId", clusterId); + Mockito.verify(sc).setParameters("type", Host.Type.Routing); + } +} diff --git a/engine/schema/src/test/java/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDaoImplTest.java b/engine/schema/src/test/java/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDaoImplTest.java index e13ad42ec808..6de8960ae747 100644 --- a/engine/schema/src/test/java/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDaoImplTest.java +++ b/engine/schema/src/test/java/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDaoImplTest.java @@ -19,11 +19,9 @@ package com.cloud.network.as.dao; -import com.cloud.network.as.AutoScaleVmGroupVmMapVO; -import com.cloud.utils.db.GenericSearchBuilder; -import com.cloud.utils.db.SearchBuilder; -import com.cloud.utils.db.SearchCriteria; -import com.cloud.vm.VirtualMachine; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import org.junit.Assert; import org.junit.Before; @@ -33,9 +31,13 @@ import org.mockito.Mockito; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; -import java.util.Arrays; -import java.util.List; +import com.cloud.network.as.AutoScaleVmGroupVmMapVO; +import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.vm.VirtualMachine; @RunWith(MockitoJUnitRunner.class) public class AutoScaleVmGroupVmMapDaoImplTest { @@ -198,4 +200,33 @@ public void testRemoveByGroupFailed() { Mockito.verify(searchCriteriaAutoScaleVmGroupVmMapVOMock).setParameters("vmGroupId", groupId); Mockito.verify(AutoScaleVmGroupVmMapDaoImplSpy).remove(searchCriteriaAutoScaleVmGroupVmMapVOMock); } + + @Test + public void testExpungeByVmListNoVms() { + Assert.assertEquals(0, AutoScaleVmGroupVmMapDaoImplSpy.expungeByVmList( + new ArrayList<>(), 100L)); + Assert.assertEquals(0, AutoScaleVmGroupVmMapDaoImplSpy.expungeByVmList( + null, 100L)); + } + + @Test + public void testExpungeByVmList() { + SearchBuilder sb = Mockito.mock(SearchBuilder.class); + SearchCriteria sc = Mockito.mock(SearchCriteria.class); + Mockito.when(sb.create()).thenReturn(sc); + Mockito.doAnswer((Answer) invocationOnMock -> { + Long batchSize = (Long)invocationOnMock.getArguments()[1]; + return batchSize == null ? 0 : batchSize.intValue(); + }).when(AutoScaleVmGroupVmMapDaoImplSpy).batchExpunge(Mockito.any(SearchCriteria.class), Mockito.anyLong()); + Mockito.when(AutoScaleVmGroupVmMapDaoImplSpy.createSearchBuilder()).thenReturn(sb); + final AutoScaleVmGroupVmMapVO mockedVO = Mockito.mock(AutoScaleVmGroupVmMapVO.class); + Mockito.when(sb.entity()).thenReturn(mockedVO); + List vmIds = List.of(1L, 2L); + Object[] array = vmIds.toArray(); + Long batchSize = 50L; + Assert.assertEquals(batchSize.intValue(), AutoScaleVmGroupVmMapDaoImplSpy.expungeByVmList(List.of(1L, 2L), batchSize)); + Mockito.verify(sc).setParameters("vmIds", array); + Mockito.verify(AutoScaleVmGroupVmMapDaoImplSpy, Mockito.times(1)) + .batchExpunge(sc, batchSize); + } } diff --git a/engine/schema/src/test/java/com/cloud/network/dao/IPAddressDaoImplTest.java b/engine/schema/src/test/java/com/cloud/network/dao/IPAddressDaoImplTest.java new file mode 100644 index 000000000000..d8f6a08d8d33 --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/network/dao/IPAddressDaoImplTest.java @@ -0,0 +1,67 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.dao; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; + +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@RunWith(MockitoJUnitRunner.class) +public class IPAddressDaoImplTest { + + @Spy + IPAddressDaoImpl ipAddressDaoImplSpy; + + @Test + public void testExpungeByVmListNoVms() { + Assert.assertEquals(0, ipAddressDaoImplSpy.expungeByVmList( + new ArrayList<>(), 100L)); + Assert.assertEquals(0, ipAddressDaoImplSpy.expungeByVmList( + null, 100L)); + } + + @Test + public void testExpungeByVmList() { + SearchBuilder sb = Mockito.mock(SearchBuilder.class); + SearchCriteria sc = Mockito.mock(SearchCriteria.class); + Mockito.when(sb.create()).thenReturn(sc); + Mockito.doAnswer((Answer) invocationOnMock -> { + Long batchSize = (Long)invocationOnMock.getArguments()[1]; + return batchSize == null ? 0 : batchSize.intValue(); + }).when(ipAddressDaoImplSpy).batchExpunge(Mockito.any(SearchCriteria.class), Mockito.anyLong()); + Mockito.when(ipAddressDaoImplSpy.createSearchBuilder()).thenReturn(sb); + final IPAddressVO mockedVO = Mockito.mock(IPAddressVO.class); + Mockito.when(sb.entity()).thenReturn(mockedVO); + List vmIds = List.of(1L, 2L); + Object[] array = vmIds.toArray(); + Long batchSize = 50L; + Assert.assertEquals(batchSize.intValue(), ipAddressDaoImplSpy.expungeByVmList(List.of(1L, 2L), batchSize)); + Mockito.verify(sc).setParameters("vmIds", array); + Mockito.verify(ipAddressDaoImplSpy, Mockito.times(1)) + .batchExpunge(sc, batchSize); + } +} diff --git a/engine/schema/src/test/java/com/cloud/network/dao/InlineLoadBalancerNicMapDaoImplTest.java b/engine/schema/src/test/java/com/cloud/network/dao/InlineLoadBalancerNicMapDaoImplTest.java new file mode 100644 index 000000000000..8e06c7618f6a --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/network/dao/InlineLoadBalancerNicMapDaoImplTest.java @@ -0,0 +1,67 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.dao; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; + +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@RunWith(MockitoJUnitRunner.class) +public class InlineLoadBalancerNicMapDaoImplTest { + + @Spy + InlineLoadBalancerNicMapDaoImpl inlineLoadBalancerNicMapDaoImplSpy; + + @Test + public void testExpungeByNicListNoVms() { + Assert.assertEquals(0, inlineLoadBalancerNicMapDaoImplSpy.expungeByNicList( + new ArrayList<>(), 100L)); + Assert.assertEquals(0, inlineLoadBalancerNicMapDaoImplSpy.expungeByNicList( + null, 100L)); + } + + @Test + public void testExpungeByNicList() { + SearchBuilder sb = Mockito.mock(SearchBuilder.class); + SearchCriteria sc = Mockito.mock(SearchCriteria.class); + Mockito.when(sb.create()).thenReturn(sc); + Mockito.doAnswer((Answer) invocationOnMock -> { + Long batchSize = (Long)invocationOnMock.getArguments()[1]; + return batchSize == null ? 0 : batchSize.intValue(); + }).when(inlineLoadBalancerNicMapDaoImplSpy).batchExpunge(Mockito.any(SearchCriteria.class), Mockito.anyLong()); + Mockito.when(inlineLoadBalancerNicMapDaoImplSpy.createSearchBuilder()).thenReturn(sb); + final InlineLoadBalancerNicMapVO mockedVO = Mockito.mock(InlineLoadBalancerNicMapVO.class); + Mockito.when(sb.entity()).thenReturn(mockedVO); + List vmIds = List.of(1L, 2L); + Object[] array = vmIds.toArray(); + Long batchSize = 50L; + Assert.assertEquals(batchSize.intValue(), inlineLoadBalancerNicMapDaoImplSpy.expungeByNicList(List.of(1L, 2L), batchSize)); + Mockito.verify(sc).setParameters("nicIds", array); + Mockito.verify(inlineLoadBalancerNicMapDaoImplSpy, Mockito.times(1)) + .batchExpunge(sc, batchSize); + } +} diff --git a/engine/schema/src/test/java/com/cloud/network/dao/LoadBalancerVMMapDaoImplTest.java b/engine/schema/src/test/java/com/cloud/network/dao/LoadBalancerVMMapDaoImplTest.java new file mode 100644 index 000000000000..fa9571949031 --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/network/dao/LoadBalancerVMMapDaoImplTest.java @@ -0,0 +1,67 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.dao; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; + +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@RunWith(MockitoJUnitRunner.class) +public class LoadBalancerVMMapDaoImplTest { + + @Spy + LoadBalancerVMMapDaoImpl loadBalancerVMMapDaoImplSpy; + + @Test + public void testExpungeByVmListNoVms() { + Assert.assertEquals(0, loadBalancerVMMapDaoImplSpy.expungeByVmList( + new ArrayList<>(), 100L)); + Assert.assertEquals(0, loadBalancerVMMapDaoImplSpy.expungeByVmList( + null, 100L)); + } + + @Test + public void testExpungeByVmList() { + SearchBuilder sb = Mockito.mock(SearchBuilder.class); + SearchCriteria sc = Mockito.mock(SearchCriteria.class); + Mockito.when(sb.create()).thenReturn(sc); + Mockito.doAnswer((Answer) invocationOnMock -> { + Long batchSize = (Long)invocationOnMock.getArguments()[1]; + return batchSize == null ? 0 : batchSize.intValue(); + }).when(loadBalancerVMMapDaoImplSpy).batchExpunge(Mockito.any(SearchCriteria.class), Mockito.anyLong()); + Mockito.when(loadBalancerVMMapDaoImplSpy.createSearchBuilder()).thenReturn(sb); + final LoadBalancerVMMapVO mockedVO = Mockito.mock(LoadBalancerVMMapVO.class); + Mockito.when(sb.entity()).thenReturn(mockedVO); + List vmIds = List.of(1L, 2L); + Object[] array = vmIds.toArray(); + Long batchSize = 50L; + Assert.assertEquals(batchSize.intValue(), loadBalancerVMMapDaoImplSpy.expungeByVmList(List.of(1L, 2L), batchSize)); + Mockito.verify(sc).setParameters("vmIds", array); + Mockito.verify(loadBalancerVMMapDaoImplSpy, Mockito.times(1)) + .batchExpunge(sc, batchSize); + } +} 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/network/dao/OpRouterMonitorServiceDaoImplTest.java b/engine/schema/src/test/java/com/cloud/network/dao/OpRouterMonitorServiceDaoImplTest.java new file mode 100644 index 000000000000..7d0b1b069baa --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/network/dao/OpRouterMonitorServiceDaoImplTest.java @@ -0,0 +1,67 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.dao; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; + +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@RunWith(MockitoJUnitRunner.class) +public class OpRouterMonitorServiceDaoImplTest { + + @Spy + OpRouterMonitorServiceDaoImpl opRouterMonitorServiceDaoImplSpy; + + @Test + public void testExpungeByVmListNoVms() { + Assert.assertEquals(0, opRouterMonitorServiceDaoImplSpy.expungeByVmList( + new ArrayList<>(), 100L)); + Assert.assertEquals(0, opRouterMonitorServiceDaoImplSpy.expungeByVmList( + null, 100L)); + } + + @Test + public void testExpungeByVmList() { + SearchBuilder sb = Mockito.mock(SearchBuilder.class); + SearchCriteria sc = Mockito.mock(SearchCriteria.class); + Mockito.when(sb.create()).thenReturn(sc); + Mockito.doAnswer((Answer) invocationOnMock -> { + Long batchSize = (Long)invocationOnMock.getArguments()[1]; + return batchSize == null ? 0 : batchSize.intValue(); + }).when(opRouterMonitorServiceDaoImplSpy).batchExpunge(Mockito.any(SearchCriteria.class), Mockito.anyLong()); + Mockito.when(opRouterMonitorServiceDaoImplSpy.createSearchBuilder()).thenReturn(sb); + final OpRouterMonitorServiceVO mockedVO = Mockito.mock(OpRouterMonitorServiceVO.class); + Mockito.when(sb.entity()).thenReturn(mockedVO); + List vmIds = List.of(1L, 2L); + Object[] array = vmIds.toArray(); + Long batchSize = 50L; + Assert.assertEquals(batchSize.intValue(), opRouterMonitorServiceDaoImplSpy.expungeByVmList(List.of(1L, 2L), batchSize)); + Mockito.verify(sc).setParameters("vmIds", array); + Mockito.verify(opRouterMonitorServiceDaoImplSpy, Mockito.times(1)) + .batchExpunge(sc, batchSize); + } +} diff --git a/engine/schema/src/test/java/com/cloud/network/rules/dao/PortForwardingRulesDaoImplTest.java b/engine/schema/src/test/java/com/cloud/network/rules/dao/PortForwardingRulesDaoImplTest.java new file mode 100644 index 000000000000..c60e9b1f1bfa --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/network/rules/dao/PortForwardingRulesDaoImplTest.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.network.rules.dao; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; + +import com.cloud.network.rules.PortForwardingRuleVO; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@RunWith(MockitoJUnitRunner.class) +public class PortForwardingRulesDaoImplTest { + + @Spy + PortForwardingRulesDaoImpl portForwardingRulesDaoImplSpy; + + @Test + public void testExpungeByVmListNoVms() { + Assert.assertEquals(0, portForwardingRulesDaoImplSpy.expungeByVmList( + new ArrayList<>(), 100L)); + Assert.assertEquals(0, portForwardingRulesDaoImplSpy.expungeByVmList( + null, 100L)); + } + + @Test + public void testExpungeByVmList() { + SearchBuilder sb = Mockito.mock(SearchBuilder.class); + SearchCriteria sc = Mockito.mock(SearchCriteria.class); + Mockito.when(sb.create()).thenReturn(sc); + Mockito.doAnswer((Answer) invocationOnMock -> { + Long batchSize = (Long)invocationOnMock.getArguments()[1]; + return batchSize == null ? 0 : batchSize.intValue(); + }).when(portForwardingRulesDaoImplSpy).batchExpunge(Mockito.any(SearchCriteria.class), Mockito.anyLong()); + Mockito.when(portForwardingRulesDaoImplSpy.createSearchBuilder()).thenReturn(sb); + final PortForwardingRuleVO mockedVO = Mockito.mock(PortForwardingRuleVO.class); + Mockito.when(sb.entity()).thenReturn(mockedVO); + List vmIds = List.of(1L, 2L); + Object[] array = vmIds.toArray(); + Long batchSize = 50L; + Assert.assertEquals(batchSize.intValue(), portForwardingRulesDaoImplSpy.expungeByVmList(List.of(1L, 2L), batchSize)); + Mockito.verify(sc).setParameters("vmIds", array); + Mockito.verify(portForwardingRulesDaoImplSpy, Mockito.times(1)) + .batchExpunge(sc, batchSize); + } +} diff --git a/engine/schema/src/test/java/com/cloud/secstorage/CommandExecLogDaoImplTest.java b/engine/schema/src/test/java/com/cloud/secstorage/CommandExecLogDaoImplTest.java new file mode 100644 index 000000000000..f86df6bdd36f --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/secstorage/CommandExecLogDaoImplTest.java @@ -0,0 +1,67 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.secstorage; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; + +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@RunWith(MockitoJUnitRunner.class) +public class CommandExecLogDaoImplTest { + + @Spy + CommandExecLogDaoImpl commandExecLogDaoImplSpy; + + @Test + public void testExpungeByVmListNoVms() { + Assert.assertEquals(0, commandExecLogDaoImplSpy.expungeByVmList( + new ArrayList<>(), 100L)); + Assert.assertEquals(0, commandExecLogDaoImplSpy.expungeByVmList( + null, 100L)); + } + + @Test + public void testExpungeByVmList() { + SearchBuilder sb = Mockito.mock(SearchBuilder.class); + SearchCriteria sc = Mockito.mock(SearchCriteria.class); + Mockito.when(sb.create()).thenReturn(sc); + Mockito.doAnswer((Answer) invocationOnMock -> { + Long batchSize = (Long)invocationOnMock.getArguments()[1]; + return batchSize == null ? 0 : batchSize.intValue(); + }).when(commandExecLogDaoImplSpy).batchExpunge(Mockito.any(SearchCriteria.class), Mockito.anyLong()); + Mockito.when(commandExecLogDaoImplSpy.createSearchBuilder()).thenReturn(sb); + final CommandExecLogVO mockedVO = Mockito.mock(CommandExecLogVO.class); + Mockito.when(sb.entity()).thenReturn(mockedVO); + List vmIds = List.of(1L, 2L); + Object[] array = vmIds.toArray(); + Long batchSize = 50L; + Assert.assertEquals(batchSize.intValue(), commandExecLogDaoImplSpy.expungeByVmList(List.of(1L, 2L), batchSize)); + Mockito.verify(sc).setParameters("vmIds", array); + Mockito.verify(commandExecLogDaoImplSpy, Mockito.times(1)) + .batchExpunge(sc, batchSize); + } +} diff --git a/engine/schema/src/test/java/com/cloud/storage/dao/VMTemplateDaoImplTest.java b/engine/schema/src/test/java/com/cloud/storage/dao/VMTemplateDaoImplTest.java new file mode 100644 index 000000000000..5cff77869be8 --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/storage/dao/VMTemplateDaoImplTest.java @@ -0,0 +1,421 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.storage.dao; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import com.cloud.cpu.CPU; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.storage.Storage; +import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.VMTemplateZoneVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.JoinBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@RunWith(MockitoJUnitRunner.class) +public class VMTemplateDaoImplTest { + + @Mock + HostDao hostDao; + + @Mock + VMTemplateZoneDao templateZoneDao; + + @Spy + @InjectMocks + VMTemplateDaoImpl templateDao = new VMTemplateDaoImpl(); + + @Test + public void testFindLatestTemplateByName_ReturnsTemplate() { + VMTemplateVO expectedTemplate = new VMTemplateVO(); + List returnedList = Collections.singletonList(expectedTemplate); + doReturn(returnedList).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class)); + VMTemplateVO result = templateDao.findLatestTemplateByName("test", 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); + } + + @Test + public void testFindLatestTemplateByName_ReturnsNullWhenNoTemplateFound() { + List emptyList = Collections.emptyList(); + doReturn(emptyList).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class)); + VMTemplateVO result = templateDao.findLatestTemplateByName("test", Hypervisor.HypervisorType.VMware, + CPU.CPUArch.getDefault()); + assertNull("Expected null when no templates are found", result); + } + + @Test + public void testFindLatestTemplateByName_NullArch() { + VMTemplateVO expectedTemplate = new VMTemplateVO(); + List returnedList = Collections.singletonList(expectedTemplate); + doReturn(returnedList).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class)); + VMTemplateVO result = templateDao.findLatestTemplateByName("test", 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); + } + + @Test + public void testGetSortedTemplatesListWithPreferredArch_PreferredProvided() { + VMTemplateVO templatePreferred = Mockito.mock(VMTemplateVO.class); + when(templatePreferred.getArch()).thenReturn(CPU.CPUArch.amd64); + VMTemplateVO templateOther = Mockito.mock(VMTemplateVO.class); + when(templateOther.getArch()).thenReturn(CPU.CPUArch.arm64); + + Map, VMTemplateVO> uniqueTemplates = new HashMap<>(); + uniqueTemplates.put(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64), templatePreferred); + uniqueTemplates.put(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.arm64), templateOther); + List sortedList = templateDao.getSortedTemplatesListWithPreferredArch(uniqueTemplates, + CPU.CPUArch.amd64.getType()); + assertEquals(2, sortedList.size()); + assertEquals(templatePreferred, sortedList.get(0)); + assertEquals(templateOther, sortedList.get(1)); + } + + @Test + public void testGetSortedTemplatesListWithPreferredArch_NoPreferred() { + VMTemplateVO template1 = Mockito.mock(VMTemplateVO.class); + when(template1.getId()).thenReturn(1L); + VMTemplateVO template2 = Mockito.mock(VMTemplateVO.class); + when(template2.getId()).thenReturn(2L); + Map, VMTemplateVO> uniqueTemplates = new HashMap<>(); + uniqueTemplates.put(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64), template1); + uniqueTemplates.put(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.arm64), template2); + List sortedList = templateDao.getSortedTemplatesListWithPreferredArch(uniqueTemplates, ""); + assertEquals(2, sortedList.size()); + assertEquals(template2, sortedList.get(0)); + assertEquals(template1, sortedList.get(1)); + } + + @Test + public void testFindSystemVMReadyTemplates() { + long zoneId = 1L; + Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM; + String preferredArch = CPU.CPUArch.arm64.getType(); + List> availableHypervisors = new ArrayList<>(); + availableHypervisors.add(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64)); + availableHypervisors.add(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.arm64)); + doReturn(availableHypervisors).when(hostDao).listDistinctHypervisorArchTypes(zoneId); + VMTemplateVO template1 = Mockito.mock(VMTemplateVO.class); + when(template1.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); + when(template1.getArch()).thenReturn(CPU.CPUArch.amd64); + VMTemplateVO template2 = Mockito.mock(VMTemplateVO.class); + when(template2.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); + when(template2.getArch()).thenReturn(CPU.CPUArch.arm64); + List templatesFromDb = Arrays.asList(template1, template2); + doReturn(templatesFromDb).when(templateDao).listBy(any(), any()); + SearchBuilder sb = mock(SearchBuilder.class); + templateDao.readySystemTemplateSearch = sb; + when(sb.create()).thenReturn(mock(SearchCriteria.class)); + List result = templateDao.findSystemVMReadyTemplates(zoneId, hypervisorType, preferredArch); + assertNotNull(result); + assertEquals(2, result.size()); + assertEquals(template2, result.get(0)); + assertEquals(template1, result.get(1)); + } + + @Test + public void testFindRoutingTemplates() { + Hypervisor.HypervisorType hType = Hypervisor.HypervisorType.KVM; + String templateName = "TestRouting"; + String preferredArch = CPU.CPUArch.amd64.getType(); + VMTemplateVO template = Mockito.mock(VMTemplateVO.class); + when(template.getArch()).thenReturn(CPU.CPUArch.amd64); + List templatesFromDb = Collections.singletonList(template); + doReturn(templatesFromDb).when(templateDao).listBy(any(), any()); + SearchBuilder sb = mock(SearchBuilder.class); + when(sb.create()).thenReturn(mock(SearchCriteria.class)); + templateDao.tmpltTypeHyperSearch2 = sb; + List result = templateDao.findRoutingTemplates(hType, templateName, preferredArch); + assertNotNull(result); + assertEquals(1, result.size()); + assertEquals(template, result.get(0)); + } + + @Test + public void testFindLatestTemplateByTypeAndHypervisorAndArch_Found() { + Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM; + CPU.CPUArch arch = CPU.CPUArch.amd64; + Storage.TemplateType type = Storage.TemplateType.SYSTEM; + VMTemplateVO template = Mockito.mock(VMTemplateVO.class); + List templatesFromDb = Collections.singletonList(template); + doReturn(templatesFromDb).when(templateDao).listBy(any(), any()); + VMTemplateVO result = templateDao.findLatestTemplateByTypeAndHypervisorAndArch(hypervisorType, arch, type); + assertNotNull(result); + assertEquals(template, result); + } + + @Test + public void testFindLatestTemplateByTypeAndHypervisorAndArch_NotFound() { + Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM; + CPU.CPUArch arch = CPU.CPUArch.x86; + Storage.TemplateType type = Storage.TemplateType.SYSTEM; + doReturn(Collections.emptyList()).when(templateDao).listBy(any(), any()); + VMTemplateVO result = templateDao.findLatestTemplateByTypeAndHypervisorAndArch(hypervisorType, arch, type); + assertNull(result); + } + + private void mockTemplateZoneJoin() { + VMTemplateZoneVO templateZoneVO = mock(VMTemplateZoneVO.class); + SearchBuilder templateZoneVOSearchBuilder = mock(SearchBuilder.class); + when(templateZoneVOSearchBuilder.entity()).thenReturn(templateZoneVO); + when(templateZoneDao.createSearchBuilder()).thenReturn(templateZoneVOSearchBuilder); + } + + @Test + public void testListTemplateIsoByArchAndZone_WithDataCenterId() { + Long dataCenterId = 1L; + CPU.CPUArch arch = CPU.CPUArch.getDefault(); + Boolean isIso = true; + VMTemplateVO templateVO = mock(VMTemplateVO.class); + GenericSearchBuilder searchBuilder = mock(GenericSearchBuilder.class); + when(searchBuilder.entity()).thenReturn(templateVO); + SearchCriteriasearchCriteria = mock(SearchCriteria.class); + when(templateDao.createSearchBuilder(Long.class)).thenReturn(searchBuilder); + when(searchBuilder.create()).thenReturn(searchCriteria); + mockTemplateZoneJoin(); + doReturn(new ArrayList<>()).when(templateDao).customSearch(searchCriteria, null); + List result = templateDao.listTemplateIsoByArchVnfAndZone(dataCenterId, arch, isIso, false); + assertNotNull(result); + verify(searchBuilder, times(1)).select(null, SearchCriteria.Func.DISTINCT, templateVO.getGuestOSId()); + verify(searchBuilder, times(1)).and(eq("state"), any(), eq(SearchCriteria.Op.IN)); + verify(searchBuilder, times(1)).and(eq("type"), any(), eq(SearchCriteria.Op.IN)); + verify(searchBuilder, times(1)).and(eq("arch"), any(), eq(SearchCriteria.Op.EQ)); + verify(searchBuilder, times(1)).and(eq("isIso"), any(), eq(SearchCriteria.Op.EQ)); + verify(searchBuilder, times(1)).join(eq("templateZoneSearch"), any(), any(), any(), eq(JoinBuilder.JoinType.INNER)); + verify(templateDao, times(1)).customSearch(searchCriteria, null); + } + + @Test + public void testListTemplateIsoByArchAndZone_WithoutDataCenterId() { + Long dataCenterId = null; + CPU.CPUArch arch = CPU.CPUArch.getDefault(); + Boolean isIso = false; + VMTemplateVO templateVO = mock(VMTemplateVO.class); + GenericSearchBuilder searchBuilder = mock(GenericSearchBuilder.class); + when(searchBuilder.entity()).thenReturn(templateVO); + SearchCriteriasearchCriteria = mock(SearchCriteria.class); + when(templateDao.createSearchBuilder(Long.class)).thenReturn(searchBuilder); + when(searchBuilder.create()).thenReturn(searchCriteria); + doReturn(new ArrayList<>()).when(templateDao).customSearch(searchCriteria, null); + List result = templateDao.listTemplateIsoByArchVnfAndZone(dataCenterId, arch, isIso, false); + assertNotNull(result); + verify(searchBuilder, times(1)).select(null, SearchCriteria.Func.DISTINCT, templateVO.getGuestOSId()); + verify(searchBuilder, times(1)).and(eq("state"), any(), eq(SearchCriteria.Op.IN)); + verify(searchBuilder, times(1)).and(eq("type"), any(), eq(SearchCriteria.Op.IN)); + verify(searchBuilder, times(1)).and(eq("arch"), any(), eq(SearchCriteria.Op.EQ)); + verify(searchBuilder, times(1)).and(eq("isIso"), any(), eq(SearchCriteria.Op.NEQ)); + verify(searchBuilder, never()).join(eq("templateZoneSearch"), any(), any(), any(), eq(JoinBuilder.JoinType.INNER)); + verify(templateDao, times(1)).customSearch(searchCriteria, null); + } + + @Test + public void testListTemplateIsoByArchAndZone_WithoutArch() { + Long dataCenterId = 1L; + CPU.CPUArch arch = null; + Boolean isIso = true; + VMTemplateVO templateVO = mock(VMTemplateVO.class); + GenericSearchBuilder searchBuilder = mock(GenericSearchBuilder.class); + when(searchBuilder.entity()).thenReturn(templateVO); + SearchCriteriasearchCriteria = mock(SearchCriteria.class); + when(templateDao.createSearchBuilder(Long.class)).thenReturn(searchBuilder); + when(searchBuilder.create()).thenReturn(searchCriteria); + mockTemplateZoneJoin(); + doReturn(new ArrayList<>()).when(templateDao).customSearch(searchCriteria, null); + List result = templateDao.listTemplateIsoByArchVnfAndZone(dataCenterId, arch, isIso, false); + assertNotNull(result); + verify(searchBuilder, times(1)).select(null, SearchCriteria.Func.DISTINCT, templateVO.getGuestOSId()); + verify(searchBuilder, times(1)).and(eq("state"), any(), eq(SearchCriteria.Op.IN)); + verify(searchBuilder, times(1)).and(eq("type"), any(), eq(SearchCriteria.Op.IN)); + verify(searchBuilder, times(1)).and(eq("arch"), any(), eq(SearchCriteria.Op.EQ)); + verify(searchBuilder, times(1)).and(eq("isIso"), any(), eq(SearchCriteria.Op.EQ)); + verify(searchBuilder, times(1)).join(eq("templateZoneSearch"), any(), any(), any(), eq(JoinBuilder.JoinType.INNER)); + verify(templateDao, times(1)).customSearch(searchCriteria, null); + } + + @Test + public void testListTemplateIsoByArchAndZone_WithoutIsIso() { + Long dataCenterId = 1L; + CPU.CPUArch arch = CPU.CPUArch.getDefault(); + Boolean isIso = null; + VMTemplateVO templateVO = mock(VMTemplateVO.class); + GenericSearchBuilder searchBuilder = mock(GenericSearchBuilder.class); + when(searchBuilder.entity()).thenReturn(templateVO); + SearchCriteriasearchCriteria = mock(SearchCriteria.class); + when(templateDao.createSearchBuilder(Long.class)).thenReturn(searchBuilder); + when(searchBuilder.create()).thenReturn(searchCriteria); + mockTemplateZoneJoin(); + doReturn(new ArrayList<>()).when(templateDao).customSearch(searchCriteria, null); + List result = templateDao.listTemplateIsoByArchVnfAndZone(dataCenterId, arch, isIso, false); + assertNotNull(result); + verify(searchBuilder, times(1)).select(null, SearchCriteria.Func.DISTINCT, templateVO.getGuestOSId()); + verify(searchBuilder, times(1)).and(eq("state"), any(), eq(SearchCriteria.Op.IN)); + verify(searchBuilder, times(1)).and(eq("type"), any(), eq(SearchCriteria.Op.IN)); + verify(searchBuilder, times(1)).and(eq("arch"), any(), eq(SearchCriteria.Op.EQ)); + verify(searchBuilder, never()).and(eq("isIso"), any(), eq(SearchCriteria.Op.NEQ)); + verify(searchBuilder, never()).and(eq("isIso"), any(), eq(SearchCriteria.Op.EQ)); + verify(searchBuilder, times(1)).join(eq("templateZoneSearch"), any(), any(), any(), eq(JoinBuilder.JoinType.INNER)); + verify(templateDao, times(1)).customSearch(searchCriteria, null); + } + + @Test + public void testListIdsByExtensionId_ReturnsIds() { + long extensionId = 42L; + List expectedIds = Arrays.asList(1L, 2L, 3L); + GenericSearchBuilder searchBuilder = mock(GenericSearchBuilder.class); + SearchCriteria searchCriteria = mock(SearchCriteria.class); + when(templateDao.createSearchBuilder(Long.class)).thenReturn(searchBuilder); + when(searchBuilder.entity()).thenReturn(mock(VMTemplateVO.class)); + when(searchBuilder.create()).thenReturn(searchCriteria); + doReturn(expectedIds).when(templateDao).customSearchIncludingRemoved(eq(searchCriteria), isNull()); + List result = templateDao.listIdsByExtensionId(extensionId); + assertEquals(expectedIds, result); + verify(searchCriteria).setParameters("extensionId", extensionId); + verify(templateDao).customSearchIncludingRemoved(eq(searchCriteria), isNull()); + } + + @Test + public void testFindSystemVMReadyTemplate() { + Long zoneId = 1L; + VMTemplateVO systemVmTemplate1 = mock(VMTemplateVO.class); + Mockito.when(systemVmTemplate1.getArch()).thenReturn(CPU.CPUArch.x86); + VMTemplateVO systemVmTemplate2 = mock(VMTemplateVO.class); + Mockito.when(systemVmTemplate2.getArch()).thenReturn(CPU.CPUArch.x86); + VMTemplateVO systemVmTemplate3 = mock(VMTemplateVO.class); + Mockito.when(systemVmTemplate3.getArch()).thenReturn(CPU.CPUArch.arm64); + Mockito.when(systemVmTemplate3.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); + List templates = Arrays.asList(systemVmTemplate1, systemVmTemplate2, systemVmTemplate3); + Mockito.when(hostDao.listDistinctHypervisorTypes(zoneId)).thenReturn(Arrays.asList(Hypervisor.HypervisorType.KVM)); + SearchBuilder sb = mock(SearchBuilder.class); + templateDao.readySystemTemplateSearch = sb; + when(sb.create()).thenReturn(mock(SearchCriteria.class)); + doReturn(templates).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class)); + VMTemplateVO readyTemplate = templateDao.findSystemVMReadyTemplate(zoneId, Hypervisor.HypervisorType.KVM, CPU.CPUArch.arm64.getType()); + Assert.assertEquals(CPU.CPUArch.arm64, readyTemplate.getArch()); + } + + @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 7968ee4a375e..9445efeb089c 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 @@ -26,16 +26,25 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.collections.CollectionUtils; import org.junit.AfterClass; +import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Mockito; +import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; +import com.cloud.storage.VolumeVO; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.TransactionLegacy; @RunWith(MockitoJUnitRunner.class) @@ -48,6 +57,7 @@ public class VolumeDaoImplTest { private static MockedStatic mockedTransactionLegacy; + @Spy private final VolumeDaoImpl volumeDao = new VolumeDaoImpl(); @BeforeClass @@ -102,4 +112,34 @@ public void testListPoolIdsByVolumeCount_without_cluster_details() throws SQLExc verify(preparedStatementMock, times(2)).setLong(anyInt(), anyLong()); verify(preparedStatementMock, times(1)).executeQuery(); } + + @Test + public void testSearchRemovedByVmsNoVms() { + Assert.assertTrue(CollectionUtils.isEmpty(volumeDao.searchRemovedByVms( + new ArrayList<>(), 100L))); + Assert.assertTrue(CollectionUtils.isEmpty(volumeDao.searchRemovedByVms( + null, 100L))); + } + + @Test + public void testSearchRemovedByVms() { + SearchBuilder sb = Mockito.mock(SearchBuilder.class); + SearchCriteria sc = Mockito.mock(SearchCriteria.class); + Mockito.when(sb.create()).thenReturn(sc); + Mockito.doReturn(new ArrayList<>()).when(volumeDao).searchIncludingRemoved( + Mockito.any(SearchCriteria.class), Mockito.any(Filter.class), Mockito.eq(null), + Mockito.eq(false)); + Mockito.when(volumeDao.createSearchBuilder()).thenReturn(sb); + final VolumeVO mockedVO = Mockito.mock(VolumeVO.class); + Mockito.when(sb.entity()).thenReturn(mockedVO); + List vmIds = List.of(1L, 2L); + Object[] array = vmIds.toArray(); + Long batchSize = 50L; + volumeDao.searchRemovedByVms(List.of(1L, 2L), batchSize); + Mockito.verify(sc).setParameters("vmIds", array); + Mockito.verify(volumeDao, Mockito.times(1)).searchIncludingRemoved( + 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/ConfigurationGroupsAggregatorTest.java b/engine/schema/src/test/java/com/cloud/upgrade/ConfigurationGroupsAggregatorTest.java new file mode 100644 index 000000000000..bab36ef00cf8 --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/upgrade/ConfigurationGroupsAggregatorTest.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 com.cloud.upgrade; + +import static org.mockito.Mockito.when; + +import java.util.Collections; + +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.config.dao.ConfigurationGroupDao; +import org.apache.cloudstack.framework.config.dao.ConfigurationSubGroupDao; +import org.apache.cloudstack.framework.config.impl.ConfigurationSubGroupVO; +import org.apache.cloudstack.framework.config.impl.ConfigurationVO; +import org.apache.logging.log4j.Logger; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class ConfigurationGroupsAggregatorTest { + @InjectMocks + private ConfigurationGroupsAggregator configurationGroupsAggregator = new ConfigurationGroupsAggregator(); + + @Mock + private ConfigurationDao configDao; + + @Mock + private ConfigurationGroupDao configGroupDao; + + @Mock + private ConfigurationSubGroupDao configSubGroupDao; + + @Mock + private Logger logger; + + @Test + public void testUpdateConfigurationGroups() { + ConfigurationVO config = new ConfigurationVO("Advanced", "DEFAULT", "management-server", + "test.config.name", null, "description"); + config.setGroupId(1L); + config.setSubGroupId(1L); + + when(configDao.searchPartialConfigurations()).thenReturn(Collections.singletonList(config)); + + ConfigurationSubGroupVO configSubGroup = Mockito.mock(ConfigurationSubGroupVO.class); + when(configSubGroupDao.findByName("name")).thenReturn(configSubGroup); + Mockito.when(configSubGroup.getId()).thenReturn(10L); + Mockito.when(configSubGroup.getGroupId()).thenReturn(5L); + + configurationGroupsAggregator.updateConfigurationGroups(); + + Assert.assertEquals(Long.valueOf(5), config.getGroupId()); + Assert.assertEquals(Long.valueOf(10), config.getSubGroupId()); + Mockito.verify(configDao, Mockito.times(1)).persist(config); + Mockito.verify(logger, Mockito.times(1)).debug("Updating configuration groups"); + Mockito.verify(logger, Mockito.times(1)).debug("Successfully updated configuration groups."); + } +} 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 d7ef18a4f32f..27995eb179af 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; @@ -43,8 +53,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 +132,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 +157,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 +204,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 +226,7 @@ public void testCalculateUpgradePathUnkownDbVersion() { } @Test - public void testCalculateUpgradePathFromKownDbVersion() { + public void testCalculateUpgradePathFromKnownDbVersion() { final CloudStackVersion dbVersion = CloudStackVersion.parse("4.17.0.0"); assertNotNull(dbVersion); @@ -223,4 +276,108 @@ public void testCalculateUpgradePathFromLatestDbVersion() { assertEquals("We should have 1 upgrade step", 1, upgrades.length); assertTrue(upgrades[0] instanceof NoopDbUpgrade); } + + @Test + public void testCalculateUpgradePathFrom41800toNextSecurityRelease() { + + final CloudStackVersion dbVersion = CloudStackVersion.parse("4.18.0.0"); + assertNotNull(dbVersion); + + final DatabaseUpgradeChecker checker = new DatabaseUpgradeChecker(); + final CloudStackVersion currentVersion = checker.getLatestVersion(); + assertNotNull(currentVersion); + + final DbUpgrade[] upgrades = checker.calculateUpgradePath(dbVersion, currentVersion); + assertNotNull(upgrades); + + final CloudStackVersion nextSecurityRelease = CloudStackVersion.parse(currentVersion.getMajorRelease() + "." + + currentVersion.getMinorRelease() + "." + + currentVersion.getPatchRelease() + "." + + (currentVersion.getSecurityRelease() + 1)); + assertNotNull(nextSecurityRelease); + + final DbUpgrade[] upgradesToNext = checker.calculateUpgradePath(dbVersion, nextSecurityRelease); + assertNotNull(upgradesToNext); + + assertEquals(upgrades.length + 1, upgradesToNext.length); + assertTrue(upgradesToNext[upgradesToNext.length - 1] instanceof NoopDbUpgrade); + } + + @Test + public void testCalculateUpgradePathFromSecurityReleaseToLatest() { + + final CloudStackVersion dbVersion = CloudStackVersion.parse("4.17.2.0"); // a EOL version + assertNotNull(dbVersion); + + final CloudStackVersion oldSecurityRelease = CloudStackVersion.parse(dbVersion.getMajorRelease() + "." + + dbVersion.getMinorRelease() + "." + + dbVersion.getPatchRelease() + "." + + (dbVersion.getSecurityRelease() + 100)); + assertNotNull(oldSecurityRelease); // fake security release 4.17.2.100 + + final DatabaseUpgradeChecker checker = new DatabaseUpgradeChecker(); + final CloudStackVersion currentVersion = checker.getLatestVersion(); + assertNotNull(currentVersion); + + final DbUpgrade[] upgrades = checker.calculateUpgradePath(dbVersion, currentVersion); + assertNotNull(upgrades); + + final DbUpgrade[] upgradesFromSecurityRelease = checker.calculateUpgradePath(oldSecurityRelease, currentVersion); + assertNotNull(upgradesFromSecurityRelease); + + assertEquals("The upgrade paths should be the same", upgrades.length, upgradesFromSecurityRelease.length); + } + + @Test + public void testCalculateUpgradePathFromSecurityReleaseToNextSecurityRelease() { + + final CloudStackVersion dbVersion = CloudStackVersion.parse("4.17.2.0"); // a EOL version + assertNotNull(dbVersion); + + final CloudStackVersion oldSecurityRelease = CloudStackVersion.parse(dbVersion.getMajorRelease() + "." + + dbVersion.getMinorRelease() + "." + + dbVersion.getPatchRelease() + "." + + (dbVersion.getSecurityRelease() + 100)); + assertNotNull(oldSecurityRelease); // fake security release 4.17.2.100 + + final DatabaseUpgradeChecker checker = new DatabaseUpgradeChecker(); + final CloudStackVersion currentVersion = checker.getLatestVersion(); + assertNotNull(currentVersion); + + final CloudStackVersion nextSecurityRelease = CloudStackVersion.parse(currentVersion.getMajorRelease() + "." + + currentVersion.getMinorRelease() + "." + + currentVersion.getPatchRelease() + "." + + (currentVersion.getSecurityRelease() + 1)); + assertNotNull(nextSecurityRelease); // fake security release + + final DbUpgrade[] upgrades = checker.calculateUpgradePath(dbVersion, currentVersion); + assertNotNull(upgrades); + + final DbUpgrade[] upgradesFromSecurityReleaseToNext = checker.calculateUpgradePath(oldSecurityRelease, nextSecurityRelease); + assertNotNull(upgradesFromSecurityReleaseToNext); + + 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()); + } + } diff --git a/engine/schema/src/test/java/com/cloud/upgrade/SystemVmTemplateRegistrationTest.java b/engine/schema/src/test/java/com/cloud/upgrade/SystemVmTemplateRegistrationTest.java new file mode 100644 index 000000000000..8028e78c9073 --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/upgrade/SystemVmTemplateRegistrationTest.java @@ -0,0 +1,1639 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES 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 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; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.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; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import com.cloud.cpu.CPU; +import com.cloud.dc.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 { + + @Mock + ClusterDao clusterDao; + + @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"; + if (StringUtils.isNotBlank(content)) { + File tempFile = File.createTempFile("metadata", ".ini"); + location = tempFile.getAbsolutePath(); + Files.write(Paths.get(location), content.getBytes()); + tempFile.deleteOnExit(); + } + mockedStatic.when(SystemVmTemplateRegistration::getMetadataFilePath).thenReturn(location); + } catch (Exception e) { + fail(e.getMessage()); + } + } + + @Test + public void test_parseMetadataFile_noFile() { + try (MockedStatic mockedStatic = + Mockito.mockStatic(SystemVmTemplateRegistration.class, Mockito.CALLS_REAL_METHODS)) { + setupMetadataFile(mockedStatic, null); + CloudRuntimeException exception = assertThrows(CloudRuntimeException.class, + SystemVmTemplateRegistration::parseMetadataFile); + assertTrue(exception.getMessage().contains("Failed to parse system VM Template metadata file")); + } + } + + @Test + public void test_parseMetadataFile_invalidContent() { + try (MockedStatic mockedStatic = + Mockito.mockStatic(SystemVmTemplateRegistration.class, Mockito.CALLS_REAL_METHODS)) { + setupMetadataFile(mockedStatic, "abc"); + CloudRuntimeException exception = assertThrows(CloudRuntimeException.class, + SystemVmTemplateRegistration::parseMetadataFile); + assertTrue(exception.getMessage().contains("Failed to parse system VM Template metadata file")); + } + } + + @Test + public void test_parseMetadataFile_success() { + String metadataFileContent = "[default]\n" + + "version = x.y.z.0\n" + + "\n" + + "[kvm-x86_64]\n" + + "templatename = systemvm-kvm-x.y.z\n" + + "checksum = abc1\n" + + "downloadurl = https://download.cloudstack.org/systemvm/x.y/systemvmtemplate-x.y.z-kvm.qcow2.bz2\n" + + "filename = systemvmtemplate-x.y.z-kvm.qcow2.bz2\n" + + "\n" + + "[kvm-aarch64]\n" + + "templatename = systemvm-kvm-x.y.z\n" + + "checksum = abc2\n" + + "downloadurl = https://download.cloudstack.org/systemvm/x.y/systemvmtemplate-x.y.z-kvm.qcow2.bz2\n" + + "filename = systemvmtemplate-x.y.z-kvm.qcow2.bz2\n" + + "\n" + + "[vmware]\n" + + "templatename = systemvm-vmware-x.y.z\n" + + "checksum = abc3\n" + + "downloadurl = https://download.cloudstack.org/systemvm/x.y/systemvmtemplate-x.y.z-vmware.ova\n" + + "filename = systemvmtemplate-x.y.z-vmware.ova\n"; + try (MockedStatic mockedStatic = + Mockito.mockStatic(SystemVmTemplateRegistration.class, Mockito.CALLS_REAL_METHODS)) { + setupMetadataFile(mockedStatic, metadataFileContent); + String version = SystemVmTemplateRegistration.parseMetadataFile(); + assertEquals("x.y.z.0", version); + } + assertNull(SystemVmTemplateRegistration.getMetadataTemplateDetails(Hypervisor.HypervisorType.XenServer, + CPU.CPUArch.getDefault())); + SystemVmTemplateRegistration.MetadataTemplateDetails templateDetails = + SystemVmTemplateRegistration.getMetadataTemplateDetails(Hypervisor.HypervisorType.KVM, + CPU.CPUArch.amd64); + assertNotNull(templateDetails); + assertEquals(CPU.CPUArch.amd64, templateDetails.getArch()); + assertEquals(Hypervisor.HypervisorType.KVM, templateDetails.getHypervisorType()); + templateDetails = + SystemVmTemplateRegistration.getMetadataTemplateDetails(Hypervisor.HypervisorType.KVM, + CPU.CPUArch.arm64); + assertNotNull(templateDetails); + assertEquals(CPU.CPUArch.arm64, templateDetails.getArch()); + assertEquals(Hypervisor.HypervisorType.KVM, templateDetails.getHypervisorType()); + templateDetails = + SystemVmTemplateRegistration.getMetadataTemplateDetails(Hypervisor.HypervisorType.VMware, + CPU.CPUArch.getDefault()); + assertNotNull(templateDetails); + assertEquals(CPU.CPUArch.getDefault(), templateDetails.getArch()); + assertEquals(Hypervisor.HypervisorType.VMware, templateDetails.getHypervisorType()); + } + + @Test + public void testMountStore_nullStoreUrl() throws Exception { + try (MockedStatic + diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolModifyStoragePoolAnswer.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolModifyStoragePoolAnswer.java index 437e786f0f61..80b87a49acbd 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolModifyStoragePoolAnswer.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolModifyStoragePoolAnswer.java @@ -36,14 +36,16 @@ public class StorPoolModifyStoragePoolAnswer extends Answer{ private List datastoreClusterChildren = new ArrayList<>(); private String clusterId; private String clientNodeId; + private String clusterLocation; - public StorPoolModifyStoragePoolAnswer(StorPoolModifyStoragePoolCommand cmd, long capacityBytes, long availableBytes, Map tInfo, String clusterId, String clientNodeId) { + public StorPoolModifyStoragePoolAnswer(StorPoolModifyStoragePoolCommand cmd, long capacityBytes, long availableBytes, Map tInfo, String clusterId, String clientNodeId, String clusterLocation) { super(cmd); result = true; poolInfo = new StoragePoolInfo(null, cmd.getPool().getHost(), cmd.getPool().getPath(), cmd.getLocalPath(), cmd.getPool().getType(), capacityBytes, availableBytes); templateInfo = tInfo; this.clusterId = clusterId; this.clientNodeId = clientNodeId; + this.clusterLocation = clusterLocation; } public StorPoolModifyStoragePoolAnswer(String errMsg) { @@ -101,4 +103,12 @@ public String getClientNodeId() { public void setClientNodeId(String clientNodeId) { this.clientNodeId = clientNodeId; } + + public String getClusterLocation() { + return clusterLocation; + } + + public void setClusterLocation(String clusterLocation) { + this.clusterLocation = clusterLocation; + } } diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolBackupSnapshotCommandWrapper.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolBackupSnapshotCommandWrapper.java index ade9e8370a8a..00746334e022 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolBackupSnapshotCommandWrapper.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolBackupSnapshotCommandWrapper.java @@ -22,12 +22,22 @@ import static com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor.SP_LOG; import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.apache.cloudstack.storage.command.CopyCmdAnswer; import org.apache.cloudstack.storage.to.SnapshotObjectTO; +import org.apache.cloudstack.utils.cryptsetup.KeyFile; +import org.apache.cloudstack.utils.qemu.QemuImageOptions; import org.apache.cloudstack.utils.qemu.QemuImg; import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; +import org.apache.cloudstack.utils.qemu.QemuImgException; import org.apache.cloudstack.utils.qemu.QemuImgFile; +import org.apache.cloudstack.utils.qemu.QemuObject; +import org.apache.cloudstack.utils.qemu.QemuObject.EncryptFormat; import org.apache.commons.io.FileUtils; import com.cloud.agent.api.storage.StorPoolBackupSnapshotCommand; @@ -57,28 +67,24 @@ public CopyCmdAnswer execute(final StorPoolBackupSnapshotCommand cmd, final Libv SP_LOG("StorpoolBackupSnapshotCommandWrapper.execute: src=" + src.getPath() + "dst=" + dst.getPath()); StorPoolStorageAdaptor.attachOrDetachVolume("attach", "snapshot", src.getPath()); srcPath = src.getPath(); - - final QemuImgFile srcFile = new QemuImgFile(srcPath, PhysicalDiskFormat.RAW); - + long size = 0; + String srcKeyName = "sec0"; + String destKeyName = "sec1"; + List qemuObjects = new ArrayList<>(); + Map options = new HashMap<>(); + QemuImageOptions qemuImageOpts = new QemuImageOptions(srcPath); + final QemuImg qemu = new QemuImg(cmd.getWaitInMillSeconds()); final DataStoreTO dstDataStore = dst.getDataStore(); if (!(dstDataStore instanceof NfsTO)) { return new CopyCmdAnswer("Backup Storpool snapshot: Only NFS secondary supported at present!"); } secondaryPool = storagePoolMgr.getStoragePoolByURI(dstDataStore.getUrl()); + try (KeyFile srcKey = new KeyFile(src.getVolume().getPassphrase())) { - final String dstDir = secondaryPool.getLocalPath() + File.separator + dst.getPath(); - FileUtils.forceMkdir(new File(dstDir)); - - final String dstPath = dstDir + File.separator + dst.getName(); - final QemuImgFile dstFile = new QemuImgFile(dstPath, PhysicalDiskFormat.QCOW2); - - final QemuImg qemu = new QemuImg(cmd.getWaitInMillSeconds()); - qemu.convert(srcFile, dstFile); - - SP_LOG("StorpoolBackupSnapshotCommandWrapper srcFileFormat=%s, dstFileFormat=%s", srcFile.getFormat(), dstFile.getFormat()); - final File snapFile = new File(dstPath); - final long size = snapFile.exists() ? snapFile.length() : 0; + size = convertSnapshot(srcPath, secondaryPool, dst, srcKeyName, qemuObjects, options, qemuImageOpts, + qemu, srcKey); + } final SnapshotObjectTO snapshot = new SnapshotObjectTO(); snapshot.setPath(dst.getPath() + File.separator + dst.getName()); @@ -86,7 +92,8 @@ public CopyCmdAnswer execute(final StorPoolBackupSnapshotCommand cmd, final Libv return new CopyCmdAnswer(snapshot); } catch (final Exception e) { - final String error = String.format("Failed to backup snapshot with id [%s] with a pool %s, due to %s", cmd.getSourceTO().getId(), cmd.getSourceTO().getDataStore().getUuid(), e.getMessage()); + final String error = String.format("Failed to backup snapshot [id: %s, name: %s] with a pool with id %s, due to %s", + cmd.getSourceTO().getId(), cmd.getSourceTO().getName(), cmd.getSourceTO().getDataStore().getUuid(), e.getMessage()); SP_LOG(error); logger.debug(error); return new CopyCmdAnswer(cmd, e); @@ -104,4 +111,31 @@ public CopyCmdAnswer execute(final StorPoolBackupSnapshotCommand cmd, final Libv } } } + + private long convertSnapshot(String srcPath, KVMStoragePool secondaryPool, final SnapshotObjectTO dst, + String srcKeyName, List qemuObjects, Map options, + QemuImageOptions qemuImageOpts, final QemuImg qemu, KeyFile srcKey) throws IOException, QemuImgException { + long size; + final QemuImgFile srcFile = new QemuImgFile(srcPath, PhysicalDiskFormat.RAW); + + final String dstDir = secondaryPool.getLocalPath() + File.separator + dst.getPath(); + FileUtils.forceMkdir(new File(dstDir)); + + final String dstPath = dstDir + File.separator + dst.getName(); + final QemuImgFile dstFile = new QemuImgFile(dstPath, PhysicalDiskFormat.QCOW2); + if (srcKey.isSet()) { + qemuObjects.add(QemuObject.prepareSecretForQemuImg(PhysicalDiskFormat.RAW, EncryptFormat.LUKS, + srcKey.toString(), srcKeyName, options)); + qemuImageOpts = new QemuImageOptions(PhysicalDiskFormat.RAW, srcPath, srcKeyName); + dstFile.setFormat(PhysicalDiskFormat.LUKS); + } + + qemuImageOpts.setImageOptsFlag(true); + qemu.convert(srcFile, dstFile, options, qemuObjects, qemuImageOpts, null, true); + + SP_LOG("StorpoolBackupSnapshotCommandWrapper srcFileFormat=%s, dstFileFormat=%s", srcFile.getFormat(), dstFile.getFormat()); + final File snapFile = new File(dstPath); + size = snapFile.exists() ? snapFile.length() : 0; + return size; + } } diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadTemplateCommandWrapper.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadTemplateCommandWrapper.java index 3e7118ab81d7..da67812cbbee 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadTemplateCommandWrapper.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadTemplateCommandWrapper.java @@ -103,9 +103,10 @@ public CopyCmdAnswer execute(final StorPoolDownloadTemplateCommand cmd, final Li final QemuImgFile srcFile = new QemuImgFile(srcDisk.getPath(), srcDisk.getFormat()); final QemuImg qemu = new QemuImg(cmd.getWaitInMillSeconds()); - StorPoolStorageAdaptor.resize( Long.toString(srcDisk.getVirtualSize()), dst.getPath()); if (dst instanceof TemplateObjectTO) { + StorPoolStorageAdaptor.resize(Long.toString(srcDisk.getVirtualSize()), dst.getPath()); + ((TemplateObjectTO) dst).setSize(srcDisk.getVirtualSize()); } diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadVolumeCommandWrapper.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadVolumeCommandWrapper.java index 37284b597d2c..de8b9484d117 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadVolumeCommandWrapper.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadVolumeCommandWrapper.java @@ -29,7 +29,6 @@ import org.apache.cloudstack.utils.qemu.QemuImg; import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; import org.apache.cloudstack.utils.qemu.QemuImgFile; -//import java.io.File; import com.cloud.agent.api.storage.StorPoolDownloadVolumeCommand; import com.cloud.agent.api.to.DataStoreTO; @@ -114,11 +113,7 @@ public CopyCmdAnswer execute(final StorPoolDownloadVolumeCommand cmd, final Libv if (isRBDPool) { KVMStoragePool srcPool = srcDisk.getPool(); String rbdDestPath = srcPool.getSourceDir() + "/" + srcDisk.getName(); - srcPath = KVMPhysicalDisk.RBDStringBuilder(srcPool.getSourceHost(), - srcPool.getSourcePort(), - srcPool.getAuthUserName(), - srcPool.getAuthSecret(), - rbdDestPath); + srcPath = KVMPhysicalDisk.RBDStringBuilder(srcPool, rbdDestPath); } else { srcPath = srcDisk.getPath(); } diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolModifyStorageCommandWrapper.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolModifyStorageCommandWrapper.java index a44ff5473ae5..8d6dcff8aed7 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolModifyStorageCommandWrapper.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolModifyStorageCommandWrapper.java @@ -24,6 +24,7 @@ import java.util.Map.Entry; import java.util.Set; +import org.apache.commons.lang3.StringUtils; import com.cloud.agent.api.Answer; import com.cloud.agent.api.storage.StorPoolModifyStoragePoolAnswer; @@ -38,7 +39,9 @@ import com.cloud.storage.template.TemplateProp; import com.cloud.utils.script.OutputInterpreter; import com.cloud.utils.script.Script; +import com.google.gson.JsonArray; import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import com.google.gson.JsonParser; @ResourceWrapper(handles = StorPoolModifyStoragePoolCommand.class) @@ -51,6 +54,7 @@ public Answer execute(final StorPoolModifyStoragePoolCommand command, final Libv logger.debug(String.format("Could not get StorPool cluster id for a command [%s]", command.getClass())); return new Answer(command, false, "spNotFound"); } + String clusterLocation = getStorPoolClusterLocation(clusterId); try { String result = attachOrDetachVolume("attach", "volume", command.getVolumeName()); if (result != null) { @@ -66,7 +70,7 @@ public Answer execute(final StorPoolModifyStoragePoolCommand command, final Libv } final Map tInfo = new HashMap<>(); - return new StorPoolModifyStoragePoolAnswer(command, storagepool.getCapacity(), storagepool.getAvailable(), tInfo, clusterId, storagepool.getStorageNodeId()); + return new StorPoolModifyStoragePoolAnswer(command, storagepool.getCapacity(), storagepool.getAvailable(), tInfo, clusterId, storagepool.getStorageNodeId(), clusterLocation); } catch (Exception e) { logger.debug(String.format("Could not modify storage due to %s", e.getMessage())); return new Answer(command, e); @@ -118,4 +122,28 @@ public String attachOrDetachVolume(String command, String type, String volumeUui } return res; } + + private String getStorPoolClusterLocation(String clusterId) { + Script sc = new Script("storpool", 300000, logger); + sc.add("-j"); + sc.add("location"); + sc.add("list"); + + OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser(); + + String res = sc.execute(parser); + if (res == null) { + JsonObject jsonObj = new JsonParser().parse(parser.getLines()).getAsJsonObject(); + if (jsonObj.getAsJsonObject("data") != null) { + JsonArray arr = jsonObj.getAsJsonObject("data").getAsJsonArray("locations"); + for (JsonElement jsonElement : arr) { + JsonObject obj = jsonElement.getAsJsonObject(); + if (StringUtils.contains(clusterId, obj.get("id").getAsString())) { + return obj.get("name").getAsString(); + } + } + } + } + return null; + } } diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java index c05d8b3ae08a..545f7b33c5fd 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java @@ -17,6 +17,32 @@ package com.cloud.hypervisor.kvm.storage; +import com.cloud.agent.api.to.DiskTO; +import com.cloud.storage.Storage; +import com.cloud.storage.Storage.ImageFormat; +import com.cloud.storage.Storage.ProvisioningType; +import com.cloud.storage.Storage.StoragePoolType; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.OutputInterpreter; +import com.cloud.utils.script.Script; + +import com.cloud.utils.storage.TemplateDownloaderUtil; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonPrimitive; + +import org.apache.cloudstack.storage.datastore.util.StorPoolUtil; +import org.apache.cloudstack.utils.qemu.QemuImg; +import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; +import org.apache.cloudstack.utils.qemu.QemuImgException; +import org.apache.cloudstack.utils.qemu.QemuImgFile; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; +import org.libvirt.LibvirtException; + import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; @@ -27,18 +53,7 @@ import java.util.List; import java.util.Map; -import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.LogManager; - -import com.cloud.agent.api.to.DiskTO; -import com.cloud.storage.Storage; -import com.cloud.storage.Storage.ImageFormat; -import com.cloud.storage.Storage.ProvisioningType; -import com.cloud.storage.Storage.StoragePoolType; -import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.utils.script.OutputInterpreter; -import com.cloud.utils.script.Script; +import java.util.UUID; public class StorPoolStorageAdaptor implements StorageAdaptor { public static void SP_LOG(String fmt, Object... args) { @@ -57,7 +72,7 @@ public static void SP_LOG(String fmt, Object... args) { private static final Map storageUuidToStoragePool = new HashMap(); @Override - public KVMStoragePool createStoragePool(String uuid, String host, int port, String path, String userInfo, StoragePoolType storagePoolType, Map details) { + public KVMStoragePool createStoragePool(String uuid, String host, int port, String path, String userInfo, StoragePoolType storagePoolType, Map details, boolean isPrimaryStorage) { SP_LOG("StorPoolStorageAdaptor.createStoragePool: uuid=%s, host=%s:%d, path=%s, userInfo=%s, type=%s", uuid, host, port, path, userInfo, storagePoolType); StorPoolStoragePool storagePool = new StorPoolStoragePool(uuid, host, port, storagePoolType, this); @@ -139,6 +154,9 @@ private static boolean waitForDeviceSymlink(String devPath) { } public static String getVolumeNameFromPath(final String volumeUuid, boolean tildeNeeded) { + if (volumeUuid == null) { + return null; + } if (volumeUuid.startsWith("/dev/storpool/")) { return volumeUuid.split("/")[3]; } else if (volumeUuid.startsWith("/dev/storpool-byid/")) { @@ -149,6 +167,10 @@ public static String getVolumeNameFromPath(final String volumeUuid, boolean tild } public static boolean attachOrDetachVolume(String command, String type, String volumeUuid) { + if (volumeUuid == null) { + LOGGER.debug("Could not attach volume. The volume ID is null"); + return false; + } final String name = getVolumeNameFromPath(volumeUuid, true); if (name == null) { return false; @@ -249,7 +271,7 @@ public KVMPhysicalDisk getPhysicalDisk(String volumeUuid, KVMStoragePool pool) { } @Override - public boolean connectPhysicalDisk(String volumeUuid, KVMStoragePool pool, Map details) { + public boolean connectPhysicalDisk(String volumeUuid, KVMStoragePool pool, Map details, boolean isVMMigrate) { SP_LOG("StorPoolStorageAdaptor.connectPhysicalDisk: uuid=%s, pool=%s", volumeUuid, pool); LOGGER.debug(String.format("connectPhysicalDisk: uuid=%s, pool=%s", volumeUuid, pool)); @@ -345,11 +367,85 @@ public boolean createFolder(String uuid, String path) { throw new UnsupportedOperationException("A folder cannot be created in this configuration."); } - public KVMPhysicalDisk createTemplateFromDirectDownloadFile(String templateFilePath, String destTemplatePath, - KVMStoragePool destPool, ImageFormat format, int timeout) { + @Override + public KVMPhysicalDisk createDiskFromTemplateBacking(KVMPhysicalDisk template, String name, + PhysicalDiskFormat format, long size, KVMStoragePool destPool, int timeout, byte[] passphrase) { return null; } + @Override + public KVMPhysicalDisk createTemplateFromDirectDownloadFile(String templateFilePath, String destTemplatePath, + KVMStoragePool destPool, ImageFormat format, int timeout) { + if (StringUtils.isEmpty(templateFilePath) || destPool == null) { + throw new CloudRuntimeException( + "Unable to create template from direct download template file due to insufficient data"); + } + + File sourceFile = new File(templateFilePath); + if (!sourceFile.exists()) { + throw new CloudRuntimeException( + "Direct download template file " + templateFilePath + " does not exist on this host"); + } + + if (!StoragePoolType.StorPool.equals(destPool.getType())) { + throw new CloudRuntimeException("Unsupported storage pool type: " + destPool.getType().toString()); + } + + if (!Storage.ImageFormat.QCOW2.equals(format)) { + throw new CloudRuntimeException("Unsupported template format: " + format.toString()); + } + + String srcTemplateFilePath = templateFilePath; + KVMPhysicalDisk destDisk = null; + QemuImgFile srcFile = null; + QemuImgFile destFile = null; + String templateName = UUID.randomUUID().toString(); + String volume = null; + try { + + srcTemplateFilePath = extractTemplate(templateFilePath, sourceFile, srcTemplateFilePath, templateName); + + QemuImg.PhysicalDiskFormat srcFileFormat = QemuImg.PhysicalDiskFormat.QCOW2; + + srcFile = new QemuImgFile(srcTemplateFilePath, srcFileFormat); + + String spTemplate = destPool.getUuid().split(";")[0]; + + QemuImg qemu = new QemuImg(timeout); + OutputInterpreter.AllLinesParser parser = createStorPoolVolume(destPool, srcFile, qemu, spTemplate); + + String response = parser.getLines(); + + LOGGER.debug(response); + volume = StorPoolUtil.devPath(getNameFromResponse(response, false, false)); + attachOrDetachVolume("attach", "volume", volume); + destDisk = destPool.getPhysicalDisk(volume); + if (destDisk == null) { + throw new CloudRuntimeException( + "Failed to find the disk: " + volume + " of the storage pool: " + destPool.getUuid()); + } + + destFile = new QemuImgFile(destDisk.getPath(), QemuImg.PhysicalDiskFormat.RAW); + + qemu.convert(srcFile, destFile); + parser = volumeSnapshot(StorPoolStorageAdaptor.getVolumeNameFromPath(volume, true), spTemplate); + response = parser.getLines(); + LOGGER.debug(response); + String newPath = StorPoolUtil.devPath(getNameFromResponse(response, false, true)); + destDisk = destPool.getPhysicalDisk(newPath); + } catch (QemuImgException | LibvirtException e) { + destDisk = null; + } finally { + if (volume != null) { + attachOrDetachVolume("detach", "volume", volume); + volumeDelete(StorPoolStorageAdaptor.getVolumeNameFromPath(volume, true)); + } + Script.runSimpleBashScript("rm -f " + srcTemplateFilePath); + } + + return destDisk; + } + @Override public boolean createFolder(String uuid, String path, String localPath) { return false; @@ -367,9 +463,87 @@ public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String n return null; } - @Override - public KVMPhysicalDisk createDiskFromTemplateBacking(KVMPhysicalDisk template, String name, - PhysicalDiskFormat format, long size, KVMStoragePool destPool, int timeout, byte[] passphrase) { - return null; + private OutputInterpreter.AllLinesParser createStorPoolVolume(KVMStoragePool destPool, QemuImgFile srcFile, + QemuImg qemu, String templateUuid) throws QemuImgException, LibvirtException { + Map info = qemu.info(srcFile); + Map reqParams = new HashMap<>(); + reqParams.put("template", templateUuid); + reqParams.put("size", info.get("virtual_size")); + Map tags = new HashMap<>(); + tags.put("cs", "template"); + reqParams.put("tags", tags); + Gson gson = new Gson(); + String js = gson.toJson(reqParams); + + Script sc = createStorPoolRequest(js, "VolumeCreate", null,true); + OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser(); + + String res = sc.execute(parser); + if (res != null) { + throw new CloudRuntimeException("Could not create volume due to: " + res); + } + return parser; + } + + private OutputInterpreter.AllLinesParser volumeSnapshot(String volumeName, String templateUuid) { + Map reqParams = new HashMap<>(); + reqParams.put("template", templateUuid); + Gson gson = new Gson(); + String js = gson.toJson(reqParams); + + Script sc = createStorPoolRequest(js, "VolumeSnapshot", volumeName,true); + OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser(); + + String res = sc.execute(parser); + if (res != null) { + throw new CloudRuntimeException("Could not snapshot volume due to: " + res); + } + return parser; + } + + private OutputInterpreter.AllLinesParser volumeDelete(String volumeName) { + Script sc = createStorPoolRequest(null, "VolumeDelete", volumeName, false); + OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser(); + + String res = sc.execute(parser); + if (res != null) { + throw new CloudRuntimeException("Could not delete volume due to: " + res); + } + return parser; + } + @NotNull + private static Script createStorPoolRequest(String js, String apiCall, String param, boolean jsonRequired) { + Script sc = new Script("storpool_req", 0, LOGGER); + sc.add("-P"); + sc.add("-M"); + if (jsonRequired) { + sc.add("--json"); + sc.add(js); + } + sc.add(apiCall); + if (param != null) { + sc.add(param); + } + return sc; + } + + private String extractTemplate(String templateFilePath, File sourceFile, String srcTemplateFilePath, + String templateName) { + if (TemplateDownloaderUtil.isTemplateExtractable(templateFilePath)) { + srcTemplateFilePath = sourceFile.getParent() + "/" + templateName; + String extractCommand = TemplateDownloaderUtil.getExtractCommandForDownloadedFile(templateFilePath, srcTemplateFilePath); + Script.runSimpleBashScript(extractCommand); + Script.runSimpleBashScript("rm -f " + templateFilePath); + } + return srcTemplateFilePath; + } + + private String getNameFromResponse(String resp, boolean tildeNeeded, boolean isSnapshot) { + JsonParser jsonParser = new JsonParser(); + JsonObject respObj = (JsonObject) jsonParser.parse(resp); + JsonPrimitive data = isSnapshot ? respObj.getAsJsonPrimitive("snapshotGlobalId") : respObj.getAsJsonPrimitive("globalId"); + String name = data !=null ? data.getAsString() : null; + name = name != null ? name.startsWith("~") && !tildeNeeded ? name.split("~")[1] : name : name; + return name; } } diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStoragePool.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStoragePool.java index aa0a8849d90f..ab5dc03d3431 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStoragePool.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStoragePool.java @@ -29,6 +29,7 @@ import com.cloud.agent.properties.AgentProperties; import com.cloud.agent.properties.AgentPropertiesFileHandler; import com.cloud.hypervisor.kvm.resource.KVMHABase.HAStoragePool; +import com.cloud.hypervisor.kvm.resource.LibvirtVMDef; import com.cloud.storage.Storage; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.utils.script.OutputInterpreter; @@ -132,7 +133,7 @@ public KVMPhysicalDisk createPhysicalDisk(String name, Storage.ProvisioningType @Override public boolean connectPhysicalDisk(String name, Map details) { - return _storageAdaptor.connectPhysicalDisk(name, this, details); + return _storageAdaptor.connectPhysicalDisk(name, this, details, false); } @Override @@ -302,4 +303,11 @@ private String executeStorPoolServiceListCmd(OutputInterpreter.AllLinesParser pa public Boolean vmActivityCheck(HAStoragePool pool, HostTO host, Duration activityScriptTimeout, String volumeUuidListString, String vmActivityCheckPath, long duration) { return checkingHeartBeat(pool, host); } + + @Override + public void customizeLibvirtDiskDef(LibvirtVMDef.DiskDef disk) { + if (LibvirtVMDef.DiskDef.DiskBus.VIRTIO.equals(disk.getBusType()) || LibvirtVMDef.DiskDef.DiskBus.SCSI.equals(disk.getBusType())) { + disk.setDiscard(LibvirtVMDef.DiskDef.DiscardType.UNMAP); + } + } } diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/collector/StorPoolAbandonObjectsCollector.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/collector/StorPoolAbandonObjectsCollector.java index 6258767921d2..7bfa6332bd15 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/collector/StorPoolAbandonObjectsCollector.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/collector/StorPoolAbandonObjectsCollector.java @@ -19,17 +19,20 @@ package org.apache.cloudstack.storage.collector; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; +import com.cloud.dc.dao.ClusterDao; -import javax.inject.Inject; +import com.cloud.storage.dao.SnapshotDetailsDao; +import com.cloud.storage.dao.SnapshotDetailsVO; +import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.concurrency.NamedThreadFactory; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallbackNoReturn; +import com.cloud.utils.db.TransactionLegacy; +import com.cloud.utils.db.TransactionStatus; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; @@ -37,27 +40,37 @@ import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; -//import org.apache.cloudstack.storage.datastore.util.StorPoolHelper; +import org.apache.cloudstack.storage.datastore.util.StorPoolHelper; import org.apache.cloudstack.storage.datastore.util.StorPoolUtil; +import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpApiResponse; +import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpConnectionDesc; + import org.apache.commons.collections.CollectionUtils; -import com.cloud.utils.component.ManagerBase; -import com.cloud.utils.concurrency.NamedThreadFactory; -import com.cloud.utils.db.DB; -import com.cloud.utils.db.Transaction; -import com.cloud.utils.db.TransactionCallbackNoReturn; -import com.cloud.utils.db.TransactionLegacy; -import com.cloud.utils.db.TransactionStatus; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; +import javax.inject.Inject; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; public class StorPoolAbandonObjectsCollector extends ManagerBase implements Configurable { @Inject private PrimaryDataStoreDao storagePoolDao; @Inject private StoragePoolDetailsDao storagePoolDetailsDao; + @Inject + private SnapshotDetailsDao snapshotDetailsDao; + @Inject + private ClusterDao clusterDao; private ScheduledExecutorService _volumeTagsUpdateExecutor; + private ScheduledExecutorService snapshotRecoveryCheckExecutor; private static final String ABANDON_LOGGER = "/var/log/cloudstack/management/storpool-abandoned-objects"; @@ -69,6 +82,9 @@ public class StorPoolAbandonObjectsCollector extends ManagerBase implements Conf "storpool.snapshot.tags.checkup", "86400", "Minimal interval (in seconds) to check and report if StorPool snapshot exists in CloudStack snapshots database", false); + static final ConfigKey snapshotRecoveryFromRemoteCheck = new ConfigKey("Advanced", Integer.class, + "storpool.snapshot.recovery.from.remote.check", "300", + "Minimal interval (in seconds) to check and recover StorPool snapshot from remote", false); @Override public String getConfigComponentName() { @@ -77,7 +93,7 @@ public String getConfigComponentName() { @Override public ConfigKey[] getConfigKeys() { - return new ConfigKey[] { volumeCheckupTagsInterval, snapshotCheckupTagsInterval }; + return new ConfigKey[] { volumeCheckupTagsInterval, snapshotCheckupTagsInterval, snapshotRecoveryFromRemoteCheck }; } @Override @@ -93,6 +109,8 @@ private void init() { } _volumeTagsUpdateExecutor = Executors.newScheduledThreadPool(2, new NamedThreadFactory("StorPoolAbandonObjectsCollector")); + snapshotRecoveryCheckExecutor = Executors.newScheduledThreadPool(1, + new NamedThreadFactory("StorPoolSnapshotRecoveryCheck")); if (volumeCheckupTagsInterval.value() > 0) { _volumeTagsUpdateExecutor.scheduleAtFixedRate(new StorPoolVolumesTagsUpdate(), @@ -102,6 +120,10 @@ private void init() { _volumeTagsUpdateExecutor.scheduleAtFixedRate(new StorPoolSnapshotsTagsUpdate(), snapshotCheckupTagsInterval.value(), snapshotCheckupTagsInterval.value(), TimeUnit.SECONDS); } + if (snapshotRecoveryFromRemoteCheck.value() > 0) { + snapshotRecoveryCheckExecutor.scheduleAtFixedRate(new StorPoolSnapshotRecoveryCheck(), + snapshotRecoveryFromRemoteCheck.value(), snapshotRecoveryFromRemoteCheck.value(), TimeUnit.SECONDS); + } } class StorPoolVolumesTagsUpdate extends ManagedContextRunnable { @@ -322,4 +344,84 @@ private Map getStorPoolNamesAndCsTag(JsonArray arr) { } return map; } + + class StorPoolSnapshotRecoveryCheck extends ManagedContextRunnable { + + @Override + protected void runInContext() { + List spPools = storagePoolDao.findPoolsByProvider(StorPoolUtil.SP_PROVIDER_NAME); + if (CollectionUtils.isEmpty(spPools)) { + return; + } + List snapshotDetails = snapshotDetailsDao.findDetails(StorPoolUtil.SP_RECOVERED_SNAPSHOT); + if (CollectionUtils.isEmpty(snapshotDetails)) { + return; + } + Map onePoolforZone = new HashMap<>(); + for (StoragePoolVO storagePoolVO : spPools) { + onePoolforZone.put(storagePoolVO.getDataCenterId(), storagePoolVO); + } + List recoveredSnapshots = new ArrayList<>(); + for (StoragePoolVO storagePool : onePoolforZone.values()) { + collectRecoveredSnapshotAfterExport(snapshotDetails, recoveredSnapshots, storagePool); + } + for (Long recoveredSnapshot : recoveredSnapshots) { + snapshotDetailsDao.remove(recoveredSnapshot); + } + } + + private void collectRecoveredSnapshotAfterExport(List snapshotDetails, List recoveredSnapshots, StoragePoolVO storagePool) { + try { + logger.debug(String.format("Checking StorPool recovered snapshots for zone [%s]", + storagePool.getDataCenterId())); + SpConnectionDesc conn = StorPoolUtil.getSpConnection(storagePool.getUuid(), + storagePool.getId(), storagePoolDetailsDao, storagePoolDao); + JsonArray arr = StorPoolUtil.snapshotsList(conn); + List snapshots = snapshotsForRecovery(arr); + if (snapshots.isEmpty()) { + return; + } + for (SnapshotDetailsVO snapshot : snapshotDetails) { + String[] snapshotOnRemote = snapshot.getValue().split(";"); + if (snapshotOnRemote.length != 2) { + continue; + } + String name = snapshot.getValue().split(";")[0]; + String location = snapshot.getValue().split(";")[1]; + if (name == null || location == null) { + StorPoolUtil.spLog("Could not find name or location for the snapshot %s", snapshot.getValue()); + continue; + } + if (snapshots.contains(name)) { + findRecoveredSnapshots(recoveredSnapshots, conn, snapshot, name, location); + } + } + } catch (Exception e) { + logger.debug(String.format("Could not collect StorPool recovered snapshots %s", e.getMessage())); + } + } + + private void findRecoveredSnapshots(List recoveredSnapshots, SpConnectionDesc conn, SnapshotDetailsVO snapshot, String name, String location) { + Long clusterId = StorPoolHelper.findClusterIdByGlobalId(StorPoolUtil.getSnapshotClusterId(name, conn), clusterDao); + conn = StorPoolHelper.getSpConnectionDesc(conn, clusterId); + SpApiResponse resp = StorPoolUtil.snapshotUnexport(name, location, conn); + if (resp.getError() == null) { + StorPoolUtil.spLog("Unexport of snapshot %s was successful", name); + recoveredSnapshots.add(snapshot.getId()); + } else { + StorPoolUtil.spLog("Could not recover StorPool snapshot %s", resp.getError()); + } + } + } + + private static List snapshotsForRecovery(JsonArray arr) { + List snapshots = new ArrayList<>(); + for (int i = 0; i < arr.size(); i++) { + boolean recoveringFromRemote = arr.get(i).getAsJsonObject().get("recoveringFromRemote").getAsBoolean(); + if (!recoveringFromRemote) { + snapshots.add(arr.get(i).getAsJsonObject().get("name").getAsString()); + } + } + return snapshots; + } } diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/api/StorPoolSnapshotDef.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/api/StorPoolSnapshotDef.java new file mode 100644 index 000000000000..26004205709b --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/api/StorPoolSnapshotDef.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.storage.datastore.api; + +import java.io.Serializable; +import java.util.Map; + +public class StorPoolSnapshotDef implements Serializable { + private static final long serialVersionUID = 1L; + private String name; + private Integer deleteAfter; + private Map tags; + private Boolean bind; + private Integer iops; + private String rename; + private transient String volumeName; + + public StorPoolSnapshotDef(String volumeName, Integer deleteAfter, Map tags) { + super(); + this.volumeName = volumeName; + this.deleteAfter = deleteAfter; + this.tags = tags; + } + + public StorPoolSnapshotDef(String name, Integer deleteAfter, Map tags, Boolean bind, Integer iops, + String rename) { + super(); + this.name = name; + this.deleteAfter = deleteAfter; + this.tags = tags; + this.bind = bind; + this.iops = iops; + this.rename = rename; + } + + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public Integer getDeleteAfter() { + return deleteAfter; + } + public void setDeleteAfter(Integer deleteAfter) { + this.deleteAfter = deleteAfter; + } + public Map getTags() { + return tags; + } + public void setTags(Map tags) { + this.tags = tags; + } + public Boolean getBind() { + return bind; + } + public void setBind(Boolean bind) { + this.bind = bind; + } + public Integer getIops() { + return iops; + } + public void setIops(Integer iops) { + this.iops = iops; + } + public String getRename() { + return rename; + } + public void setRename(String rename) { + this.rename = rename; + } + + public String getVolumeName() { + return volumeName; + } + + public void setVolumeName(String volumeName) { + this.volumeName = volumeName; + } + +} diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/api/StorPoolVolumeDef.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/api/StorPoolVolumeDef.java new file mode 100644 index 000000000000..456f5b90639c --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/api/StorPoolVolumeDef.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.storage.datastore.api; + +import java.io.Serializable; +import java.util.Map; + +public class StorPoolVolumeDef implements Serializable { + + private static final long serialVersionUID = 1L; + private transient String name; + private Long size; + private Map tags; + private String parent; + private Long iops; + private String template; + private String baseOn; + private String rename; + private Boolean shrinkOk; + + public StorPoolVolumeDef() { + } + + public StorPoolVolumeDef(String name, Long size, Map tags, String parent, Long iops, String template, + String baseOn, String rename, Boolean shrinkOk) { + super(); + this.name = name; + this.size = size; + this.tags = tags; + this.parent = parent; + this.iops = iops; + this.template = template; + this.baseOn = baseOn; + this.rename = rename; + this.shrinkOk = shrinkOk; + } + + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public Long getSize() { + return size; + } + public void setSize(Long size) { + this.size = size; + } + public Map getTags() { + return tags; + } + public void setTags(Map tags) { + this.tags = tags; + } + public String getParent() { + return parent; + } + public void setParent(String parent) { + this.parent = parent; + } + public Long getIops() { + return iops; + } + public void setIops(Long iops) { + this.iops = iops; + } + public String getTemplate() { + return template; + } + public void setTemplate(String template) { + this.template = template; + } + public String getBaseOn() { + return baseOn; + } + public void setBaseOn(String baseOn) { + this.baseOn = baseOn; + } + public String getRename() { + return rename; + } + public void setRename(String rename) { + this.rename = rename; + } + + public Boolean getShrinkOk() { + return shrinkOk; + } + + public void setShrinkOk(Boolean shrinkOk) { + this.shrinkOk = shrinkOk; + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorPoolPrimaryDataStoreDriver.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorPoolPrimaryDataStoreDriver.java index f7e643ca62b5..f666711f7443 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorPoolPrimaryDataStoreDriver.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorPoolPrimaryDataStoreDriver.java @@ -18,30 +18,52 @@ */ package org.apache.cloudstack.storage.datastore.driver; +import com.cloud.storage.dao.SnapshotDetailsVO; + +import java.util.HashMap; import java.util.List; import java.util.Map; import javax.inject.Inject; +import com.cloud.storage.dao.StoragePoolHostDao; +import com.cloud.storage.dao.VMTemplateDetailsDao; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.storage.dao.VolumeDetailsDao; +import com.cloud.tags.dao.ResourceTagDao; +import com.cloud.utils.Pair; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine.State; +import com.cloud.vm.VirtualMachineManager; +import com.cloud.vm.dao.VMInstanceDao; + import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo; import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult; 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.DataStoreCapabilities; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.resourcedetail.DiskOfferingDetailVO; +import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao; import org.apache.cloudstack.storage.RemoteHostEndPoint; import org.apache.cloudstack.storage.command.CommandResult; import org.apache.cloudstack.storage.command.CopyCmdAnswer; import org.apache.cloudstack.storage.command.CreateObjectAnswer; import org.apache.cloudstack.storage.command.StorageSubSystemCommand; +import org.apache.cloudstack.storage.datastore.api.StorPoolSnapshotDef; +import org.apache.cloudstack.storage.datastore.api.StorPoolVolumeDef; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; @@ -60,6 +82,7 @@ import org.apache.cloudstack.storage.to.TemplateObjectTO; import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.cloudstack.storage.volume.VolumeObject; + import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.MapUtils; @@ -78,14 +101,22 @@ import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.StorageFilerTO; import com.cloud.dc.dao.ClusterDao; +import com.cloud.exception.StorageUnavailableException; import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor; +import com.cloud.offering.DiskOffering; import com.cloud.server.ResourceTag; import com.cloud.server.ResourceTag.ResourceObjectType; +import com.cloud.service.ServiceOfferingDetailsVO; +import com.cloud.service.ServiceOfferingVO; +import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.service.dao.ServiceOfferingDetailsDao; import com.cloud.storage.DataStoreRole; import com.cloud.storage.ResizeVolumePayload; +import com.cloud.storage.Snapshot; +import com.cloud.storage.SnapshotVO; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; @@ -94,19 +125,9 @@ import com.cloud.storage.Volume; import com.cloud.storage.VolumeDetailVO; import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.SnapshotDetailsDao; -import com.cloud.storage.dao.SnapshotDetailsVO; -import com.cloud.storage.dao.StoragePoolHostDao; -import com.cloud.storage.dao.VMTemplateDetailsDao; -import com.cloud.storage.dao.VolumeDao; -import com.cloud.storage.dao.VolumeDetailsDao; -import com.cloud.tags.dao.ResourceTagDao; -import com.cloud.utils.Pair; -import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.vm.VMInstanceVO; -import com.cloud.vm.VirtualMachine.State; -import com.cloud.vm.VirtualMachineManager; -import com.cloud.vm.dao.VMInstanceDao; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -133,9 +154,11 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver { @Inject private HostDao hostDao; @Inject - private ResourceTagDao _resourceTagDao; + private ResourceTagDao resourceTagDao; @Inject - private SnapshotDetailsDao _snapshotDetailsDao; + private SnapshotDetailsDao snapshotDetailsDao; + @Inject + private SnapshotDao snapshotDao; @Inject private SnapshotDataStoreDao snapshotDataStoreDao; @Inject @@ -148,6 +171,14 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver { private StoragePoolHostDao storagePoolHostDao; @Inject DataStoreManager dataStoreManager; + @Inject + private DiskOfferingDetailsDao diskOfferingDetailsDao; + @Inject + private ServiceOfferingDetailsDao serviceOfferingDetailDao; + @Inject + private ServiceOfferingDao serviceOfferingDao; + @Inject + private VolumeDataFactory volumeDataFactory; private SnapshotDataStoreVO getSnapshotImageStoreRef(long snapshotId, long zoneId) { List snaps = snapshotDataStoreDao.listReadyBySnapshot(snapshotId, DataStoreRole.Image); @@ -161,7 +192,10 @@ private SnapshotDataStoreVO getSnapshotImageStoreRef(long snapshotId, long zoneI @Override public Map getCapabilities() { - return null; + Map mapCapabilities = new HashMap<>(); + mapCapabilities.put(DataStoreCapabilities.CAN_COPY_SNAPSHOT_BETWEEN_ZONES_AND_SAME_POOL_TYPE.toString(), Boolean.TRUE.toString()); + mapCapabilities.put(DataStoreCapabilities.CAN_CREATE_TEMPLATE_FROM_SNAPSHOT.toString(), Boolean.TRUE.toString()); + return mapCapabilities; } @Override @@ -199,7 +233,6 @@ public void revokeAccess(DataObject data, Host host, DataStore dataStore) { StorPoolUtil.volumeRemoveTags(StorPoolStorageAdaptor.getVolumeNameFromPath(volume.getPath(), true), conn); } } - } private void updateStoragePool(final long poolId, final long deltaUsedBytes) { @@ -251,15 +284,25 @@ public ChapInfo getChapInfo(DataObject dataObject) { public void createAsync(DataStore dataStore, DataObject data, AsyncCompletionCallback callback) { String path = null; Answer answer; + String tier = null; + String template = null; if (data.getType() == DataObjectType.VOLUME) { try { VolumeInfo vinfo = (VolumeInfo)data; String name = vinfo.getUuid(); Long size = vinfo.getPassphraseId() == null ? vinfo.getSize() : vinfo.getSize() + 2097152; + Long vmId = vinfo.getInstanceId(); + SpConnectionDesc conn = StorPoolUtil.getSpConnection(dataStore.getUuid(), dataStore.getId(), storagePoolDetailsDao, primaryStoreDao); - StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriver.createAsync volume: name=%s, uuid=%s, isAttached=%s vm=%s, payload=%s, template: %s", vinfo.getName(), vinfo.getUuid(), vinfo.isAttachedVM(), vinfo.getAttachedVmName(), vinfo.getpayload(), conn.getTemplateName()); - SpApiResponse resp = StorPoolUtil.volumeCreate(name, null, size, getVMInstanceUUID(vinfo.getInstanceId()), null, "volume", vinfo.getMaxIops(), conn); + if (vinfo.getDiskOfferingId() != null) { + tier = getTierFromOfferingDetail(vinfo.getDiskOfferingId()); + if (tier == null) { + template = getTemplateFromOfferingDetail(vinfo.getDiskOfferingId()); + } + } + + SpApiResponse resp = createStorPoolVolume(template, tier, vinfo, name, size, vmId, conn); if (resp.getError() == null) { String volumeName = StorPoolUtil.getNameFromResponse(resp, false); path = StorPoolUtil.devPath(volumeName); @@ -290,6 +333,21 @@ public void createAsync(DataStore dataStore, DataObject data, AsyncCompletionCal } } + private SpApiResponse createStorPoolVolume(String template, String tier, VolumeInfo vinfo, String name, Long size, + Long vmId, SpConnectionDesc conn) { + SpApiResponse resp = new SpApiResponse(); + Map tags = StorPoolHelper.addStorPoolTags(name, getVMInstanceUUID(vmId), "volume", getVcPolicyTag(vmId), tier); + if (vinfo.getDeviceId() != null) { + tags.put("disk", vinfo.getDeviceId().toString()); + } + if (template == null) { + template = conn.getTemplateName(); + } + StorPoolVolumeDef volume = new StorPoolVolumeDef(null, size, tags, null, vinfo.getMaxIops(), template, null, null, null); + resp = StorPoolUtil.volumeCreate(volume, conn); + return resp; + } + private void updateVolume(DataStore dataStore, String path, VolumeInfo vinfo) { VolumeVO volume = volumeDao.findById(vinfo.getId()); volume.setPoolId(dataStore.getId()); @@ -328,66 +386,118 @@ private StorPoolSetVolumeEncryptionAnswer createEncryptedVolume(DataStore dataSt public void resize(DataObject data, AsyncCompletionCallback callback) { String path = null; String err = null; - ResizeVolumeAnswer answer = null; if (data.getType() == DataObjectType.VOLUME) { VolumeObject vol = (VolumeObject)data; - StoragePool pool = (StoragePool)data.getDataStore(); - ResizeVolumePayload payload = (ResizeVolumePayload)vol.getpayload(); + path = vol.getPath(); - final String name = StorPoolStorageAdaptor.getVolumeNameFromPath(vol.getPath(), true); - final long oldSize = vol.getSize(); - Long oldMaxIops = vol.getMaxIops(); - - try { - SpConnectionDesc conn = StorPoolUtil.getSpConnection(data.getDataStore().getUuid(), data.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); + err = resizeVolume(data, path, vol); + } else { + err = String.format("Invalid object type \"%s\" passed to resize", data.getType()); + } - long maxIops = payload.newMaxIops == null ? Long.valueOf(0) : payload.newMaxIops; + CreateCmdResult res = new CreateCmdResult(path, new Answer(null, err != null, err)); + res.setResult(err); + callback.complete(res); + } - StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.resize: name=%s, uuid=%s, oldSize=%d, newSize=%s, shrinkOk=%s, maxIops=%s", name, vol.getUuid(), oldSize, payload.newSize, payload.shrinkOk, maxIops); + private String resizeVolume(DataObject data, String path, VolumeObject vol) { + String err = null; + ResizeVolumePayload payload = (ResizeVolumePayload)vol.getpayload(); + boolean needResize = vol.getSize() != payload.newSize; - SpApiResponse resp = StorPoolUtil.volumeUpdate(name, payload.newSize, payload.shrinkOk, maxIops, conn); - if (resp.getError() != null) { - err = String.format("Could not resize StorPool volume %s. Error: %s", name, resp.getError()); - } else { - StorPoolResizeVolumeCommand resizeCmd = new StorPoolResizeVolumeCommand(vol.getPath(), new StorageFilerTO(pool), vol.getSize(), payload.newSize, payload.shrinkOk, - payload.instanceName, payload.hosts == null ? false : true); - answer = (ResizeVolumeAnswer) storageMgr.sendToPool(pool, payload.hosts, resizeCmd); + final String name = StorPoolStorageAdaptor.getVolumeNameFromPath(path, true); + final long oldSize = vol.getSize(); + Long oldMaxIops = vol.getMaxIops(); - if (answer == null || !answer.getResult()) { - err = answer != null ? answer.getDetails() : "return a null answer, resize failed for unknown reason"; - } else { - path = StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false)); - - vol.setSize(payload.newSize); - vol.update(); - if (payload.newMaxIops != null) { - VolumeVO volume = volumeDao.findById(vol.getId()); - volume.setMaxIops(payload.newMaxIops); - volumeDao.update(volume.getId(), volume); - } + try { + SpConnectionDesc conn = StorPoolUtil.getSpConnection(data.getDataStore().getUuid(), data.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); - updateStoragePool(vol.getPoolId(), payload.newSize - oldSize); - } - } - if (err != null) { - // try restoring volume to its initial size - resp = StorPoolUtil.volumeUpdate(name, oldSize, true, oldMaxIops, conn); - if (resp.getError() != null) { - logger.debug(String.format("Could not resize StorPool volume %s back to its original size. Error: %s", name, resp.getError())); - } + err = updateStorPoolVolume(vol, payload, conn); + if (err == null && needResize) { + err = notifyQemuForTheNewSize(data, err, vol, payload); + } + if (err != null) { + // try restoring volume to its initial size + SpApiResponse response = StorPoolUtil.volumeUpdate(name, oldSize, true, oldMaxIops, conn); + if (response.getError() != null) { + logger.debug(String.format("Could not resize StorPool volume %s back to its original size. Error: %s", name, response.getError())); } - } catch (Exception e) { - logger.debug("sending resize command failed", e); - err = e.toString(); + } else { + updateVolumeWithTheNewSize(vol, payload); + } + } catch (Exception e) { + logger.debug("sending resize command failed", e); + err = e.toString(); + } + return err; + } + + private void updateVolumeWithTheNewSize(VolumeObject vol, ResizeVolumePayload payload) { + vol.setSize(payload.newSize); + vol.update(); + if (payload.newMaxIops != null) { + VolumeVO volume = volumeDao.findById(vol.getId()); + volume.setMaxIops(payload.newMaxIops); + volumeDao.update(volume.getId(), volume); + } + updateStoragePool(vol.getPoolId(), payload.newSize - vol.getSize()); + } + + private String notifyQemuForTheNewSize(DataObject data, String err, VolumeObject vol, ResizeVolumePayload payload) + throws StorageUnavailableException { + StoragePool pool = (StoragePool)data.getDataStore(); + + StorPoolResizeVolumeCommand resizeCmd = new StorPoolResizeVolumeCommand(vol.getPath(), new StorageFilerTO(pool), vol.getSize(), payload.newSize, payload.shrinkOk, + payload.instanceName, payload.hosts == null ? false : true); + ResizeVolumeAnswer answer = (ResizeVolumeAnswer) storageMgr.sendToPool(pool, payload.hosts, resizeCmd); + + if (answer == null || !answer.getResult()) { + err = answer != null ? answer.getDetails() : "return a null answer, resize failed for unknown reason"; + } + return err; + } + + private String updateStorPoolVolume(VolumeObject vol, ResizeVolumePayload payload, SpConnectionDesc conn) { + String err = null; + String name = StorPoolStorageAdaptor.getVolumeNameFromPath(vol.getPath(), true); + Long newDiskOfferingId = payload.getNewDiskOfferingId(); + String tier = null; + String template = null; + if (newDiskOfferingId != null) { + tier = getTierFromOfferingDetail(newDiskOfferingId); + if (tier == null) { + template = getTemplateFromOfferingDetail(newDiskOfferingId); } + } + SpApiResponse resp = new SpApiResponse(); + if (tier != null || template != null) { + resp = updateVolumeByStorPoolQoS(payload, conn, name, tier, template); } else { - err = String.format("Invalid object type \"%s\" passed to resize", data.getType()); + resp = updateVolumeByOffering(vol, payload, conn, name); + } + if (resp.getError() != null) { + err = String.format("Could not resize StorPool volume %s. Error: %s", name, resp.getError()); } + return err; + } - CreateCmdResult res = new CreateCmdResult(path, answer); - res.setResult(err); - callback.complete(res); + private static SpApiResponse updateVolumeByStorPoolQoS(ResizeVolumePayload payload, SpConnectionDesc conn, String name, String tier, String template) { + Map tags = StorPoolHelper.addStorPoolTags(null, null, null, null, tier); + StorPoolVolumeDef spVolume = new StorPoolVolumeDef(name, payload.newSize, tags, null, null, template, null, null, + payload.shrinkOk); + return StorPoolUtil.volumeUpdate(spVolume, conn); + } + + private static SpApiResponse updateVolumeByOffering(VolumeObject vol, ResizeVolumePayload payload, SpConnectionDesc conn, String name) { + long maxIops = payload.newMaxIops == null ? Long.valueOf(0) : payload.newMaxIops; + + StorPoolVolumeDef spVolume = new StorPoolVolumeDef(name, payload.newSize, null, null, maxIops, null, null, null, + payload.shrinkOk); + StorPoolUtil.spLog( + "StorpoolPrimaryDataStoreDriverImpl.resize: name=%s, uuid=%s, oldSize=%d, newSize=%s, shrinkOk=%s, maxIops=%s", + name, vol.getUuid(), vol.getSize(), payload.newSize, payload.shrinkOk, maxIops); + return StorPoolUtil.volumeUpdate(spVolume, conn); } @Override @@ -402,7 +512,7 @@ public void deleteAsync(DataStore dataStore, DataObject data, AsyncCompletionCal } try { SpConnectionDesc conn = StorPoolUtil.getSpConnection(dataStore.getUuid(), dataStore.getId(), storagePoolDetailsDao, primaryStoreDao); - + tryToSnapshotVolumeBeforeDelete(vinfo, dataStore, name, conn); SpApiResponse resp = StorPoolUtil.volumeDelete(name, conn); if (resp.getError() == null) { updateStoragePool(dataStore.getId(), - vinfo.getSize()); @@ -418,6 +528,8 @@ public void deleteAsync(DataStore dataStore, DataObject data, AsyncCompletionCal } catch (Exception e) { err = String.format("Could not delete volume due to %s", e.getMessage()); } + } else if (data.getType() == DataObjectType.SNAPSHOT) { + err = deleteSnapshot((SnapshotInfo) data, err); } else { err = String.format("Invalid DataObjectType \"%s\" passed to deleteAsync", data.getType()); } @@ -432,6 +544,66 @@ public void deleteAsync(DataStore dataStore, DataObject data, AsyncCompletionCal callback.complete(res); } + private String deleteSnapshot(SnapshotInfo data, String err) { + SnapshotInfo snapshot = data; + SpConnectionDesc conn = StorPoolUtil.getSpConnection(snapshot.getDataStore().getUuid(), snapshot.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); + String name = StorPoolStorageAdaptor.getVolumeNameFromPath(snapshot.getPath(), true); + SpApiResponse resp = StorPoolUtil.snapshotDelete(name, conn); + if (resp.getError() != null) { + err = String.format("Failed to clean-up Storpool snapshot %s. Error: %s", name, resp.getError()); + StorPoolUtil.spLog(err); + } + return err; + } + + private void tryToSnapshotVolumeBeforeDelete(VolumeInfo vinfo, DataStore dataStore, String name, SpConnectionDesc conn) { + Integer deleteAfter = StorPoolConfigurationManager.DeleteAfterInterval.valueIn(dataStore.getId()); + if (deleteAfter != null && deleteAfter > 0 && vinfo.getPassphraseId() == null) { + createTemporarySnapshot(vinfo, name, deleteAfter, conn); + } else { + StorPoolUtil.spLog("The volume [%s] is not marked to be snapshot. Check the global setting `storpool.delete.after.interval` or the volume is encrypted [%s]", name, deleteAfter, vinfo.getPassphraseId() != null); + } + } + + private void createTemporarySnapshot(VolumeInfo vinfo, String name, Integer deleteAfter, SpConnectionDesc conn) { + Map tags = new HashMap<>(); + tags.put("cs", StorPoolUtil.DELAY_DELETE); + StorPoolSnapshotDef snapshot = new StorPoolSnapshotDef(name, deleteAfter, tags); + StorPoolUtil.spLog("Creating backup snapshot before delete the volume [%s]", vinfo.getName()); + SpApiResponse snapshotResponse = StorPoolUtil.volumeSnapshot(snapshot, conn); + if (snapshotResponse.getError() == null) { + String snapshotName = StorPoolUtil.getSnapshotNameFromResponse(snapshotResponse, false, StorPoolUtil.GLOBAL_ID); + String snapshotPath = StorPoolUtil.devPath(snapshotName); + SnapshotVO snapshotVo = createSnapshotVo(vinfo, snapshotName); + createSnapshotOnPrimaryVo(vinfo, snapshotVo, snapshotPath); + SnapshotDetailsVO snapshotDetails = new SnapshotDetailsVO(snapshotVo.getId(), StorPoolUtil.SP_DELAY_DELETE, "~" + snapshotName, true); + snapshotDetailsDao.persist(snapshotDetails); + } + } + + private void createSnapshotOnPrimaryVo(VolumeInfo vinfo, SnapshotVO snapshotVo, String snapshotPath) { + SnapshotDataStoreVO snapshotOnPrimaryVo = new SnapshotDataStoreVO(); + snapshotOnPrimaryVo.setSnapshotId(snapshotVo.getId()); + snapshotOnPrimaryVo.setDataStoreId(vinfo.getDataCenterId()); + snapshotOnPrimaryVo.setRole(vinfo.getDataStore().getRole()); + snapshotOnPrimaryVo.setVolumeId(vinfo.getId()); + snapshotOnPrimaryVo.setSize(vinfo.getSize()); + snapshotOnPrimaryVo.setPhysicalSize(vinfo.getSize()); + snapshotOnPrimaryVo.setInstallPath(snapshotPath); + snapshotOnPrimaryVo.setState(ObjectInDataStoreStateMachine.State.Ready); + snapshotDataStoreDao.persist(snapshotOnPrimaryVo); + } + + private SnapshotVO createSnapshotVo(VolumeInfo vinfo, String snapshotName) { + SnapshotVO snapshotVo = new SnapshotVO(vinfo.getDataCenterId(), vinfo.getAccountId(), vinfo.getDomainId(), vinfo.getId(), + vinfo.getDiskOfferingId(), snapshotName, + (short)Snapshot.Type.RECURRING.ordinal(), Snapshot.Type.RECURRING.name(), + vinfo.getSize(), vinfo.getMinIops(), vinfo.getMaxIops(), vinfo.getHypervisorType(), Snapshot.LocationType.PRIMARY); + snapshotVo.setState(com.cloud.storage.Snapshot.State.BackedUp); + snapshotVo = snapshotDao.persist(snapshotVo); + return snapshotVo; + } + private void logDataObject(final String pref, DataObject data) { final DataStore dstore = data.getDataStore(); String name = null; @@ -456,7 +628,22 @@ private void logDataObject(final String pref, DataObject data) { @Override public boolean canCopy(DataObject srcData, DataObject dstData) { - return true; + DataObjectType srcType = srcData.getType(); + DataObjectType dstType = dstData.getType(); + if (srcType == DataObjectType.SNAPSHOT && dstType == DataObjectType.VOLUME) { + return true; + } else if (srcType == DataObjectType.SNAPSHOT && dstType == DataObjectType.SNAPSHOT) { + return true; + } else if (srcType == DataObjectType.VOLUME && dstType == DataObjectType.TEMPLATE) { + return true; + } else if (srcType == DataObjectType.TEMPLATE && dstType == DataObjectType.TEMPLATE) { + return true; + } else if (srcType == DataObjectType.TEMPLATE && dstType == DataObjectType.VOLUME) { + return true; + } else if (srcType == DataObjectType.VOLUME && dstType == DataObjectType.VOLUME) { + return true; + } + return false; } @Override @@ -474,83 +661,67 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal try { if (srcType == DataObjectType.SNAPSHOT && dstType == DataObjectType.VOLUME) { SnapshotInfo sinfo = (SnapshotInfo)srcData; - final String snapshotName = StorPoolHelper.getSnapshotName(srcData.getId(), srcData.getUuid(), snapshotDataStoreDao, _snapshotDetailsDao); - VolumeInfo vinfo = (VolumeInfo)dstData; final String volumeName = vinfo.getUuid(); final Long size = vinfo.getSize(); + SpConnectionDesc conn = StorPoolUtil.getSpConnection(vinfo.getDataStore().getUuid(), vinfo.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); - SpApiResponse resp = StorPoolUtil.volumeCreate(volumeName, snapshotName, size, null, null, "volume", sinfo.getBaseVolume().getMaxIops(), conn); + String snapshotName = StorPoolStorageAdaptor.getVolumeNameFromPath(((SnapshotInfo) srcData).getPath(), true); + + StorPoolVolumeDef spVolume = createVolumeWithTags(sinfo, snapshotName, vinfo, volumeName, size, conn); + SpApiResponse resp = StorPoolUtil.volumeCreate(spVolume, conn); if (resp.getError() == null) { updateStoragePool(dstData.getDataStore().getId(), size); VolumeObjectTO to = (VolumeObjectTO)dstData.getTO(); to.setPath(StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false))); to.setSize(size); + updateVolumePoolType(vinfo); answer = new CopyCmdAnswer(to); - StorPoolUtil.spLog("Created volume=%s with uuid=%s from snapshot=%s with uuid=%s", StorPoolUtil.getNameFromResponse(resp, false), to.getUuid(), snapshotName, sinfo.getUuid()); + StorPoolUtil.spLog("Created volume=%s with uuid=%s from snapshot=%s with uuid=%s", StorPoolUtil.getNameFromResponse(resp, false), volumeName, snapshotName, sinfo.getUuid()); } else if (resp.getError().getName().equals("objectDoesNotExist")) { //check if snapshot is on secondary storage - StorPoolUtil.spLog("Snapshot %s does not exists on StorPool, will try to create a volume from a snopshot on secondary storage", snapshotName); + StorPoolUtil.spLog("Snapshot %s does not exists on StorPool, will try to create a volume from a snapshot on secondary storage", snapshotName); SnapshotDataStoreVO snap = getSnapshotImageStoreRef(sinfo.getId(), vinfo.getDataCenterId()); - if (snap != null && StorPoolStorageAdaptor.getVolumeNameFromPath(snap.getInstallPath(), false) == null) { - resp = StorPoolUtil.volumeCreate(srcData.getUuid(), null, size, null, "no", "snapshot", sinfo.getBaseVolume().getMaxIops(), conn); - if (resp.getError() == null) { - VolumeObjectTO dstTO = (VolumeObjectTO) dstData.getTO(); - dstTO.setSize(size); - dstTO.setPath(StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false))); - cmd = new StorPoolDownloadTemplateCommand(srcData.getTO(), dstTO, StorPoolHelper.getTimeout(StorPoolHelper.PrimaryStorageDownloadWait, configDao), VirtualMachineManager.ExecuteInSequence.value(), "volume"); - - EndPoint ep = selector.select(srcData, dstData); - if (ep == null) { - err = "No remote endpoint to send command, check if host or ssvm is down?"; - } else { - answer = ep.sendMessage(cmd); - } - - if (answer != null && answer.getResult()) { - SpApiResponse resp2 = StorPoolUtil.volumeFreeze(StorPoolUtil.getNameFromResponse(resp, true), conn); - if (resp2.getError() != null) { - err = String.format("Could not freeze Storpool volume %s. Error: %s", srcData.getUuid(), resp2.getError()); - } else { - String name = StorPoolUtil.getNameFromResponse(resp, false); - SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(sinfo.getId(), sinfo.getUuid()); - if (snapshotDetails != null) { - StorPoolHelper.updateSnapshotDetailsValue(snapshotDetails.getId(), StorPoolUtil.devPath(name), "snapshot"); - }else { - StorPoolHelper.addSnapshotDetails(sinfo.getId(), sinfo.getUuid(), StorPoolUtil.devPath(name), _snapshotDetailsDao); - } - resp = StorPoolUtil.volumeCreate(volumeName, StorPoolUtil.getNameFromResponse(resp, true), size, null, null, "volume", sinfo.getBaseVolume().getMaxIops(), conn); - if (resp.getError() == null) { - updateStoragePool(dstData.getDataStore().getId(), size); - - VolumeObjectTO to = (VolumeObjectTO) dstData.getTO(); - to.setPath(StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false))); - to.setSize(size); - // successfully downloaded snapshot to primary storage - answer = new CopyCmdAnswer(to); - StorPoolUtil.spLog("Created volume=%s with uuid=%s from snapshot=%s with uuid=%s", name, to.getUuid(), snapshotName, sinfo.getUuid()); - - } else { - err = String.format("Could not create Storpool volume %s from snapshot %s. Error: %s", volumeName, snapshotName, resp.getError()); - } - } - } else { - err = answer != null ? answer.getDetails() : "Unknown error while downloading template. Null answer returned."; - } + SnapshotDetailsVO snapshotDetail = snapshotDetailsDao.findDetail(sinfo.getId(), StorPoolUtil.SP_DELAY_DELETE); + if (snapshotDetail != null) { + answer = new Answer(cmd, false, String.format("Could not create volume from snapshot due to: %s. The snapshot was created with the delayDelete option.", resp.getError())); + } else if (snap != null && StorPoolStorageAdaptor.getVolumeNameFromPath(snap.getInstallPath(), false) == null) { + spVolume.setParent(null); + SpApiResponse emptyVolumeCreateResp = StorPoolUtil.volumeCreate(spVolume, conn); + if (emptyVolumeCreateResp.getError() == null) { + answer = createVolumeFromSnapshot(srcData, dstData, size, emptyVolumeCreateResp); } else { - err = String.format("Could not create Storpool volume %s from snapshot %s. Error: %s", volumeName, snapshotName, resp.getError()); + answer = new Answer(cmd, false, String.format("Could not create Storpool volume %s from snapshot %s. Error: %s", volumeName, snapshotName, emptyVolumeCreateResp.getError())); } + VolumeObjectTO to = (VolumeObjectTO) dstData.getTO(); + to.setPath(StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false))); + to.setSize(size); + + answer = new CopyCmdAnswer(to); + StorPoolUtil.spLog("Created volume=%s with uuid=%s from snapshot=%s with uuid=%s", StorPoolUtil.getNameFromResponse(resp, false), to.getUuid(), snapshotName, sinfo.getUuid()); } else { - err = String.format("The snapshot %s does not exists neither on primary, neither on secondary storage. Cannot create volume from snapshot", snapshotName); + err = String.format("Could not create volume from a snapshot due to {}", resp.getError()); + } + } else if (sinfo.getDataStore().getRole().equals(DataStoreRole.Image)) { + //check if snapshot is on secondary storage + StorPoolUtil.spLog("Snapshot %s does not exists on StorPool, will try to create a volume from a snapshot on secondary storage", sinfo.getName()); + SnapshotDataStoreVO snap = getSnapshotImageStoreRef(sinfo.getId(), vinfo.getDataCenterId()); + SpApiResponse emptyVolumeCreateResp = StorPoolUtil.volumeCreate(volumeName, null, size, null, null, "volume", null, conn); + if (emptyVolumeCreateResp.getError() == null) { + answer = createVolumeFromSnapshot(srcData, dstData, size, emptyVolumeCreateResp); + } else { + answer = new Answer(cmd, false, String.format("Could not create Storpool volume %s from snapshot %s. Error: %s", volumeName, snapshotName, emptyVolumeCreateResp.getError())); } } else { - err = String.format("Could not create Storpool volume %s from snapshot %s. Error: %s", volumeName, snapshotName, resp.getError()); + answer = new Answer(cmd, false, String.format("Could not create Storpool volume %s from snapshot %s. Error: %s", volumeName, snapshotName, resp.getError())); } } else if (srcType == DataObjectType.SNAPSHOT && dstType == DataObjectType.SNAPSHOT) { + SnapshotInfo sinfo = (SnapshotInfo)srcData; + SnapshotDetailsVO snapshotDetail = snapshotDetailsDao.findDetail(sinfo.getId(), StorPoolUtil.SP_DELAY_DELETE); // bypass secondary storage - if (StorPoolConfigurationManager.BypassSecondaryStorage.value()) { + if (Boolean.FALSE.equals(SnapshotInfo.BackupSnapshotAfterTakingSnapshot.value())) { SnapshotObjectTO snapshot = (SnapshotObjectTO) srcData.getTO(); answer = new CopyCmdAnswer(snapshot); } else { @@ -560,9 +731,9 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal final String snapName = StorPoolStorageAdaptor.getVolumeNameFromPath(((SnapshotInfo) srcData).getPath(), true); SpConnectionDesc conn = StorPoolUtil.getSpConnection(srcData.getDataStore().getUuid(), srcData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); try { - Long clusterId = StorPoolHelper.findClusterIdByGlobalId(snapName, clusterDao); - EndPoint ep = clusterId != null ? RemoteHostEndPoint.getHypervisorHostEndPoint(StorPoolHelper.findHostByCluster(clusterId, hostDao)) : selector.select(srcData, dstData); - if (ep == null) { + Long clusterId = StorPoolHelper.findClusterIdByGlobalId(StorPoolUtil.getSnapshotClusterId(snapName, conn), clusterDao); + HostVO host = clusterId != null ? StorPoolHelper.findHostByCluster(clusterId, hostDao) : null; + EndPoint ep = host != null ? RemoteHostEndPoint.getHypervisorHostEndPoint(host) : selector.select(srcData, dstData); if (ep == null) { err = "No remote endpoint to send command, check if host or ssvm is down?"; } else { answer = ep.sendMessage(cmd); @@ -594,8 +765,7 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal StorPoolHelper.getTimeout(StorPoolHelper.PrimaryStorageDownloadWait, configDao), VirtualMachineManager.ExecuteInSequence.value()); try { - Long clusterId = StorPoolHelper.findClusterIdByGlobalId(volumeName, clusterDao); - EndPoint ep2 = clusterId != null ? RemoteHostEndPoint.getHypervisorHostEndPoint(StorPoolHelper.findHostByCluster(clusterId, hostDao)) : selector.select(srcData, dstData); + EndPoint ep2 = selector.select(srcData, dstData); if (ep2 == null) { err = "No remote endpoint to send command, check if host or ssvm is down?"; } else { @@ -642,22 +812,17 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal err = String.format("Could not create Storpool volume for CS template %s. Error: %s", name, resp.getError()); } else { String volumeNameToSnapshot = StorPoolUtil.getNameFromResponse(resp, true); - SpApiResponse resp2 = StorPoolUtil.volumeFreeze(volumeNameToSnapshot, conn); - if (resp2.getError() != null) { - err = String.format("Could not freeze Storpool volume %s. Error: %s", name, resp2.getError()); - } else { - StorPoolUtil.spLog("Storpool snapshot [%s] for a template exists. Creating template on Storpool with name [%s]", tinfo.getUuid(), name); - TemplateObjectTO dstTO = (TemplateObjectTO) dstData.getTO(); - dstTO.setPath(StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false))); - dstTO.setSize(size); - answer = new CopyCmdAnswer(dstTO); - } + TemplateObjectTO dstTO = (TemplateObjectTO) dstData.getTO(); + + answer = createVolumeSnapshot(cmd, size, conn, volumeNameToSnapshot, dstTO); + StorPoolUtil.volumeDelete(volumeNameToSnapshot, conn); } } else { resp = StorPoolUtil.volumeCreate(name, null, size, null, "no", "template", null, conn); if (resp.getError() != null) { err = String.format("Could not create Storpool volume for CS template %s. Error: %s", name, resp.getError()); } else { + String volName = StorPoolUtil.getNameFromResponse(resp, true); TemplateObjectTO dstTO = (TemplateObjectTO)dstData.getTO(); dstTO.setPath(StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false))); dstTO.setSize(size); @@ -673,19 +838,13 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal if (answer != null && answer.getResult()) { // successfully downloaded template to primary storage - SpApiResponse resp2 = StorPoolUtil.volumeFreeze(StorPoolUtil.getNameFromResponse(resp, true), conn); - if (resp2.getError() != null) { - err = String.format("Could not freeze Storpool volume %s. Error: %s", name, resp2.getError()); - } + TemplateObjectTO templ = (TemplateObjectTO) ((CopyCmdAnswer) answer).getNewData(); + answer = createVolumeSnapshot(cmd, size, conn, volName, templ); } else { err = answer != null ? answer.getDetails() : "Unknown error while downloading template. Null answer returned."; } - } - } - if (err != null) { - resp = StorPoolUtil.volumeDelete(StorPoolUtil.getNameFromResponse(resp, true), conn); - if (resp.getError() != null) { - logger.warn(String.format("Could not clean-up Storpool volume %s. Error: %s", name, resp.getError())); + + StorPoolUtil.volumeDelete(volName, conn); } } } else if (srcType == DataObjectType.TEMPLATE && dstType == DataObjectType.VOLUME) { @@ -711,8 +870,34 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal } StorPoolUtil.spLog(String.format("volume size is: %d", size)); Long vmId = vinfo.getInstanceId(); - SpApiResponse resp = StorPoolUtil.volumeCreate(name, parentName, size, getVMInstanceUUID(vmId), getVcPolicyTag(vmId), - "volume", vinfo.getMaxIops(), conn); + + String template = null; + String tier = null; + SpApiResponse resp = new SpApiResponse(); + + if (vinfo.getDiskOfferingId() != null) { + tier = getTierFromOfferingDetail(vinfo.getDiskOfferingId()); + if (tier == null) { + template = getTemplateFromOfferingDetail(vinfo.getDiskOfferingId()); + } + StorPoolUtil.spLog( + "Creating volume [%s] with template [%s] or tier tags [%s] described in disk/service offerings details", + vinfo.getUuid(), template, tier); + } + + Map tags = StorPoolHelper.addStorPoolTags(name, getVMInstanceUUID(vmId), "volume", getVcPolicyTag(vmId), tier); + + if (vinfo.getDeviceId() != null) { + tags.put("disk", vinfo.getDeviceId().toString()); + } + + if (template == null) { + template = conn.getTemplateName(); + } + + StorPoolVolumeDef volumeDef = new StorPoolVolumeDef(null, size, tags, parentName, null, template, null, null, null); + resp = StorPoolUtil.volumeCreate(volumeDef, conn); + if (resp.getError() == null) { updateStoragePool(dstData.getDataStore().getId(), vinfo.getSize()); updateVolumePoolType(vinfo); @@ -749,7 +934,6 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal if (resp.getError() != null) { err = String.format("Could not create Storpool volume for CS template %s. Error: %s", name, resp.getError()); } else { - //updateVolume(dstData.getId()); VolumeObjectTO dstTO = (VolumeObjectTO)dstData.getTO(); dstTO.setPath(StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false))); dstTO.setSize(size); @@ -804,8 +988,9 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc command=%s ", cmd); try { - Long clusterId = StorPoolHelper.findClusterIdByGlobalId(snapshotName, clusterDao); - EndPoint ep = clusterId != null ? RemoteHostEndPoint.getHypervisorHostEndPoint(StorPoolHelper.findHostByCluster(clusterId, hostDao)) : selector.select(srcData, dstData); + Long clusterId = StorPoolHelper.findClusterIdByGlobalId(StorPoolUtil.getSnapshotClusterId(snapshotName, conn), clusterDao); + HostVO host = clusterId != null ? StorPoolHelper.findHostByCluster(clusterId, hostDao) : null; + EndPoint ep = host != null ? RemoteHostEndPoint.getHypervisorHostEndPoint(host) : selector.select(srcData, dstData); StorPoolUtil.spLog("selector.select(srcData, dstData) ", ep); if (ep == null) { ep = selector.select(dstData); @@ -856,6 +1041,62 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal callback.complete(res); } + private StorPoolVolumeDef createVolumeWithTags(SnapshotInfo sinfo, String snapshotName, VolumeInfo vinfo, String volumeName, Long size, SpConnectionDesc conn) { + Pair templateAndTier = getTemplateAndTier(vinfo, conn); + Map tags = StorPoolHelper.addStorPoolTags(volumeName, getVMInstanceUUID(vinfo.getInstanceId()), "volume", getVcPolicyTag(vinfo.getInstanceId()), templateAndTier.first()); + return new StorPoolVolumeDef(null, size, tags, snapshotName, sinfo.getBaseVolume().getMaxIops(), templateAndTier.second(), null, null, null); + } + + private Pair getTemplateAndTier(VolumeInfo vinfo, SpConnectionDesc conn) { + String tier = null; + String template = null; + if (vinfo.getDiskOfferingId() != null) { + tier = getTierFromOfferingDetail(vinfo.getDiskOfferingId()); + if (tier == null) { + template = getTemplateFromOfferingDetail(vinfo.getDiskOfferingId()); + } + } + + if (template == null) { + template = conn.getTemplateName(); + } + return new Pair<>(tier, template); + } + private Answer createVolumeSnapshot(StorageSubSystemCommand cmd, Long size, SpConnectionDesc conn, + String volName, TemplateObjectTO dstTO) { + Answer answer; + SpApiResponse resp = StorPoolUtil.volumeSnapshot(volName, dstTO.getUuid(), null, "template", null, conn); + if (resp.getError() != null) { + answer = new Answer(cmd, false, String.format("Could not snapshot volume. Error: %s", resp.getError())); + } else { + dstTO.setPath(StorPoolUtil.devPath( + StorPoolUtil.getSnapshotNameFromResponse(resp, false, StorPoolUtil.GLOBAL_ID))); + answer = new CopyCmdAnswer(dstTO); + } + return answer; + } + + private Answer createVolumeFromSnapshot(DataObject srcData, DataObject dstData, final Long size, + SpApiResponse emptyVolumeCreateResp) { + Answer answer; + String name = StorPoolUtil.getNameFromResponse(emptyVolumeCreateResp, false); + VolumeObjectTO dstTO = (VolumeObjectTO) dstData.getTO(); + dstTO.setSize(size); + dstTO.setPath(StorPoolUtil.devPath(name)); + StorageSubSystemCommand cmd = new StorPoolDownloadTemplateCommand(srcData.getTO(), dstTO, StorPoolHelper.getTimeout(StorPoolHelper.PrimaryStorageDownloadWait, configDao), VirtualMachineManager.ExecuteInSequence.value(), "volume"); + + EndPoint ep = selector.select(srcData, dstData); + if (ep == null) { + answer = new Answer(cmd, false, "\"No remote endpoint to send command, check if host or ssvm is down?\""); + } else { + answer = ep.sendMessage(cmd); + } + if (answer == null || !answer.getResult()) { + answer = new Answer(cmd, false, answer != null ? answer.getDetails() : "Unknown error while downloading template. Null answer returned."); + } + return answer; + } + private void updateVolumePoolType(VolumeInfo vinfo) { VolumeVO volumeVO = volumeDao.findById(vinfo.getId()); volumeVO.setPoolType(StoragePoolType.StorPool); @@ -987,9 +1228,9 @@ public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback callback) { final VolumeInfo vinfo = snapshot.getBaseVolume(); - final String snapshotName = StorPoolHelper.getSnapshotName(snapshot.getId(), snapshot.getUuid(), snapshotDataStoreDao, _snapshotDetailsDao); + final String snapshotName = StorPoolHelper.getSnapshotName(snapshot.getId(), snapshot.getUuid(), snapshotDataStoreDao, snapshotDetailsDao); final String volumeName = StorPoolStorageAdaptor.getVolumeNameFromPath(vinfo.getPath(), true); StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.revertSnapshot: snapshot: name=%s, uuid=%s, volume: name=%s, uuid=%s", snapshotName, snapshot.getUuid(), volumeName, vinfo.getUuid()); String err = null; @@ -1059,7 +1300,7 @@ public void revertSnapshot(final SnapshotInfo snapshot, final SnapshotInfo snaps } private String getVcPolicyTag(Long vmId) { - ResourceTag resourceTag = vmId != null ? _resourceTagDao.findByKey(vmId, ResourceObjectType.UserVm, StorPoolUtil.SP_VC_POLICY) : null; + ResourceTag resourceTag = vmId != null ? resourceTagDao.findByKey(vmId, ResourceObjectType.UserVm, StorPoolUtil.SP_VC_POLICY) : null; return resourceTag != null ? resourceTag.getValue() : ""; } @@ -1113,7 +1354,7 @@ public Pair getVolumeStats(StoragePool storagePool, String volumeId) return volumeStats; } } else { - List volumes = volumeDao.findByPoolId(storagePool.getId()); + List volumes = volumeDao.findNonDestroyedVolumesByPoolId(storagePool.getId()); for (VolumeVO volume : volumes) { if (volume.getPath() != null && volume.getPath().equals(volumeId)) { long size = volume.getSize(); @@ -1141,18 +1382,33 @@ public void provideVmInfo(long vmId, long volumeId) { return; } StoragePoolVO poolVO = primaryStoreDao.findById(volume.getPoolId()); - if (poolVO != null) { - try { - SpConnectionDesc conn = StorPoolUtil.getSpConnection(poolVO.getUuid(), poolVO.getId(), storagePoolDetailsDao, primaryStoreDao); - String volName = StorPoolStorageAdaptor.getVolumeNameFromPath(volume.getPath(), true); - VMInstanceVO userVM = vmInstanceDao.findById(vmId); - SpApiResponse resp = StorPoolUtil.volumeUpdateIopsAndTags(volName, volume.getInstanceId() != null ? userVM.getUuid() : "", null, conn, getVcPolicyTag(vmId)); - if (resp.getError() != null) { - logger.warn(String.format("Could not update VC policy tags of a volume with id [%s]", volume.getUuid())); - } - } catch (Exception e) { - logger.warn(String.format("Could not update Virtual machine tags due to %s", e.getMessage())); + if (poolVO != null && StoragePoolType.StorPool.equals(poolVO.getPoolType())) { + VolumeInfo vInfo = volumeDataFactory.getVolume(volumeId); + if (vInfo == null) { + StorPoolUtil.spLog("Could not find volume with volume ID [%s] to set tags", volumeId); + return; + } + updateVolumeWithTags(poolVO, vInfo); + } + } + + private void updateVolumeWithTags(StoragePoolVO poolVO, VolumeInfo vInfo) { + try { + SpConnectionDesc conn = StorPoolUtil.getSpConnection(poolVO.getUuid(), poolVO.getId(), storagePoolDetailsDao, primaryStoreDao); + String volName = StorPoolStorageAdaptor.getVolumeNameFromPath(vInfo.getPath(), true); + Pair templateAndTier = getTemplateAndTier(vInfo, conn); + Map tags = StorPoolHelper.addStorPoolTags(volName, getVMInstanceUUID(vInfo.getInstanceId()), "volume", getVcPolicyTag(vInfo.getInstanceId()), templateAndTier.first()); + if (vInfo.getDeviceId() != null) { + tags.put("disk", vInfo.getDeviceId().toString()); } + StorPoolVolumeDef spVolume = new StorPoolVolumeDef(volName, null, tags, null, null, templateAndTier.second(), null, null, null); + StorPoolUtil.spLog("Updating volume's tags [%s] with template [%s]", tags, templateAndTier.second()); + SpApiResponse resp = StorPoolUtil.volumeUpdate(spVolume, conn); + if (resp.getError() != null) { + logger.warn(String.format("Could not update VC policy tags of a volume with id [%s]", vInfo.getUuid())); + } + } catch (Exception e) { + logger.warn(String.format("Could not update Virtual machine tags due to %s", e.getMessage())); } } @@ -1194,4 +1450,67 @@ public void detachVolumeFromAllStorageNodes(Volume volume) { StorPoolUtil.spLog("The volume [%s] is detach from all clusters [%s]", volName, resp); } } + + @Override + public boolean informStorageForDiskOfferingChange() { + return true; + } + + @Override + public void updateStorageWithTheNewDiskOffering(Volume volume, DiskOffering newDiskOffering) { + if (newDiskOffering == null) { + return; + } + + StoragePoolVO pool = primaryStoreDao.findById(volume.getPoolId()); + if (pool == null) { + return; + } + + String tier = getTierFromOfferingDetail(newDiskOffering.getId()); + String template = null; + if (tier == null) { + template = getTemplateFromOfferingDetail(newDiskOffering.getId()); + } + if (tier == null && template == null) { + return; + } + SpConnectionDesc conn = StorPoolUtil.getSpConnection(pool.getUuid(), pool.getId(), storagePoolDetailsDao, primaryStoreDao); + StorPoolUtil.spLog("Updating volume [%s] with tier tag [%s] or template [%s] from Disk offering", volume.getId(), tier, template); + String volumeName = StorPoolStorageAdaptor.getVolumeNameFromPath(volume.getPath(), true); + Map tags = StorPoolHelper.addStorPoolTags(null, null, null, null, tier); + StorPoolVolumeDef spVolume = new StorPoolVolumeDef(volumeName, null, tags, null, null, template, null, null, null); + SpApiResponse response = StorPoolUtil.volumeUpdate(spVolume, conn); + if (response.getError() != null) { + StorPoolUtil.spLog("Could not update volume [%s] with tier tag [%s] or template [%s] from Disk offering due to [%s]", volume.getId(), tier, template, response.getError()); + } + } + + private String getTemplateFromOfferingDetail(Long diskOfferingId) { + String template = null; + DiskOfferingDetailVO diskOfferingDetail = diskOfferingDetailsDao.findDetail(diskOfferingId, StorPoolUtil.SP_TEMPLATE); + if (diskOfferingDetail == null ) { + ServiceOfferingVO serviceOffering = serviceOfferingDao.findServiceOfferingByComputeOnlyDiskOffering(diskOfferingId, true); + if (serviceOffering != null) { + ServiceOfferingDetailsVO serviceOfferingDetail = serviceOfferingDetailDao.findDetail(serviceOffering.getId(), StorPoolUtil.SP_TEMPLATE); + if (serviceOfferingDetail != null) { + template = serviceOfferingDetail.getValue(); + } + } + } else { + template = diskOfferingDetail.getValue(); + } + return template; + } + + private String getTierFromOfferingDetail(Long diskOfferingId) { + String tier = null; + DiskOfferingDetailVO diskOfferingDetail = diskOfferingDetailsDao.findDetail(diskOfferingId, StorPoolUtil.SP_TIER); + if (diskOfferingDetail == null ) { + return tier; + } else { + tier = diskOfferingDetail.getValue(); + } + return tier; + } } diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorPoolStatsCollector.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorPoolStatsCollector.java index a41ff66229c7..a2c8b274ce95 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorPoolStatsCollector.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorPoolStatsCollector.java @@ -28,18 +28,28 @@ import javax.inject.Inject; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.util.StorPoolUtil; import org.apache.cloudstack.storage.snapshot.StorPoolConfigurationManager; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import com.cloud.storage.DataStoreRole; +import com.cloud.storage.SnapshotVO; +import com.cloud.storage.dao.SnapshotDao; +import com.cloud.storage.dao.SnapshotDetailsDao; +import com.cloud.storage.dao.SnapshotDetailsVO; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.concurrency.NamedThreadFactory; +import com.cloud.utils.exception.CloudRuntimeException; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -52,6 +62,12 @@ public class StorPoolStatsCollector extends ManagerBase { private StoragePoolDetailsDao storagePoolDetailsDao; @Inject private ConfigurationDao configurationDao; + @Inject + private SnapshotDao snapshotDao; + @Inject + private SnapshotDataStoreDao snapshotDataStoreDao; + @Inject + private SnapshotDetailsDao snapshotDetailsDao; private ScheduledExecutorService executor; @@ -67,7 +83,7 @@ enum StorPoolObject { public boolean start() { List spPools = storagePoolDao.findPoolsByProvider(StorPoolUtil.SP_PROVIDER_NAME); if (CollectionUtils.isNotEmpty(spPools)) { - executor = Executors.newScheduledThreadPool(2,new NamedThreadFactory("StorPoolStatsCollector")); + executor = Executors.newScheduledThreadPool(3, new NamedThreadFactory("StorPoolStatsCollector")); long storageStatsInterval = NumbersUtil.parseLong(configurationDao.getValue("storage.stats.interval"), 60000L); long volumeStatsInterval = NumbersUtil.parseLong(configurationDao.getValue("volume.stats.interval"), 60000L); @@ -77,6 +93,13 @@ public boolean start() { if (StorPoolConfigurationManager.StorageStatsInterval.value() > 0 && storageStatsInterval > 0) { executor.scheduleAtFixedRate(new StorPoolStorageStatsMonitorTask(), 120, StorPoolConfigurationManager.StorageStatsInterval.value(), TimeUnit.SECONDS); } + for (StoragePoolVO pool: spPools) { + Integer deleteAfter = StorPoolConfigurationManager.DeleteAfterInterval.valueIn(pool.getId()); + if (deleteAfter != null && deleteAfter > 0) { + executor.scheduleAtFixedRate(new StorPoolSnapshotsWithDelayDelete(), 120, StorPoolConfigurationManager.ListSnapshotsWithDeleteAfterInterval.value(), TimeUnit.SECONDS); + break; + } + } } return true; @@ -123,12 +146,12 @@ public void run() { } for (StoragePoolVO storagePool : onePoolforZone.values()) { try { - logger.debug(String.format("Collecting templates statistics for zone [%s]", storagePool.getDataCenterId())); + logger.debug(String.format("Collecting Templates statistics for zone [%s]", storagePool.getDataCenterId())); JsonArray arr = StorPoolUtil.templatesStats(StorPoolUtil.getSpConnection(storagePool.getUuid(), storagePool.getId(), storagePoolDetailsDao, storagePoolDao)); templatesStats.put(storagePool.getDataCenterId(), getClusterVolumeOrTemplateSpace(arr, StorPoolObject.TEMPLATE)); } catch (Exception e) { - logger.debug(String.format("Could not collect StorPool templates statistics %s", e.getMessage())); + logger.debug(String.format("Could not collect StorPool Templates statistics %s", e.getMessage())); } } } @@ -182,4 +205,90 @@ private void getClusterStats(JsonArray data, Map> map) } } } + + class StorPoolSnapshotsWithDelayDelete implements Runnable { + + @Override + public void run() { + List spPools = storagePoolDao.findPoolsByProvider(StorPoolUtil.SP_PROVIDER_NAME); + if (CollectionUtils.isNotEmpty(spPools)) { + Map onePoolForZone = new HashMap<>(); + for (StoragePoolVO storagePoolVO : spPools) { + onePoolForZone.put(storagePoolVO.getDataCenterId(), storagePoolVO); + } + for (StoragePoolVO storagePool : onePoolForZone.values()) { + List snapshotsDetails = snapshotDetailsDao.findDetailsByZoneAndKey(storagePool.getDataCenterId(), StorPoolUtil.SP_DELAY_DELETE); + if (CollectionUtils.isEmpty(snapshotsDetails)) { + return; + } + Map snapshotsWithDelayDelete = new HashMap<>(); + + try { + logger.debug(String.format("Collecting snapshots marked to be deleted for zone [%s]", storagePool.getDataCenterId())); + JsonArray arr = StorPoolUtil.snapshotsListAllClusters(StorPoolUtil.getSpConnection(storagePool.getUuid(), + storagePool.getId(), storagePoolDetailsDao, storagePoolDao)); + snapshotsWithDelayDelete.putAll(getSnapshotsMarkedForDeletion(arr)); + logger.debug(String.format("Found snapshot details [%s] and snapshots on StorPool with delay delete flag [%s]", snapshotsDetails, snapshotsWithDelayDelete)); + syncSnapshots(snapshotsDetails, snapshotsWithDelayDelete); + } catch (Exception e) { + logger.debug("Could not fetch the snapshots with delay delete flag " + e.getMessage()); + } + } + } + } + + private void syncSnapshots(List snapshotsDetails, + Map snapshotsWithDelayDelete) { + for (SnapshotDetailsVO snapshotDetailsVO : snapshotsDetails) { + if (!snapshotsWithDelayDelete.containsKey(snapshotDetailsVO.getValue())) { + StorPoolUtil.spLog("The snapshot [%s] with delayDelete flag is no longer on StorPool. Removing it from CloudStack", snapshotDetailsVO.getValue()); + SnapshotDataStoreVO ss = snapshotDataStoreDao + .findBySourceSnapshot(snapshotDetailsVO.getResourceId(), DataStoreRole.Primary); + if (ss != null) { + ss.setState(State.Destroyed); + snapshotDataStoreDao.update(ss.getId(), ss); + } + SnapshotVO snap = snapshotDao.findById(snapshotDetailsVO.getResourceId()); + if (snap != null) { + snap.setState(com.cloud.storage.Snapshot.State.Destroyed); + snapshotDao.update(snap.getId(), snap); + } + snapshotDetailsDao.remove(snapshotDetailsVO.getId()); + } + } + } + + private Map getSnapshotsMarkedForDeletion(JsonArray arr) { + for (JsonElement jsonElement : arr) { + JsonObject error = jsonElement.getAsJsonObject().getAsJsonObject("error"); + if (error != null) { + throw new CloudRuntimeException(String.format("Could not collect the snapshots marked for deletion from all storage nodes due to: [%s]", error)); + } + } + Map snapshotsWithDelayDelete = new HashMap<>(); + for (JsonElement jsonElement : arr) { + JsonObject response = jsonElement.getAsJsonObject().getAsJsonObject("response"); + if (response == null) { + return snapshotsWithDelayDelete; + } + collectSnapshots(snapshotsWithDelayDelete, response); + } + logger.debug("Found snapshots on StorPool" + snapshotsWithDelayDelete); + return snapshotsWithDelayDelete; + } + + private void collectSnapshots(Map snapshotsWithDelayDelete, JsonObject response) { + JsonArray snapshots = response.getAsJsonObject().getAsJsonArray("data"); + for (JsonElement snapshot : snapshots) { + String name = snapshot.getAsJsonObject().get("name").getAsString(); + JsonObject tags = snapshot.getAsJsonObject().get("tags").getAsJsonObject(); + if (!StringUtils.startsWith(name, "*") && StringUtils.containsNone(name, "@") && tags != null && !tags.entrySet().isEmpty()) { + String tag = tags.getAsJsonPrimitive("cs").getAsString(); + if (tag != null && tag.equals(StorPoolUtil.DELAY_DELETE)) { + snapshotsWithDelayDelete.put(name, tag); + } + } + } + } + } } diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/StorPoolPrimaryDataStoreLifeCycle.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/StorPoolPrimaryDataStoreLifeCycle.java index 339ee625c581..d299fe34ffc2 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/StorPoolPrimaryDataStoreLifeCycle.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/StorPoolPrimaryDataStoreLifeCycle.java @@ -24,6 +24,7 @@ import javax.inject.Inject; +import com.cloud.storage.dao.StoragePoolAndAccessGroupMapDao; import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.HostScope; @@ -38,8 +39,6 @@ import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpApiResponse; import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpConnectionDesc; import org.apache.cloudstack.storage.volume.datastore.PrimaryDataStoreHelper; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.LogManager; import com.cloud.agent.api.StoragePoolInfo; import com.cloud.host.HostVO; @@ -61,9 +60,7 @@ import com.cloud.storage.dao.VMTemplatePoolDao; import com.cloud.utils.exception.CloudRuntimeException; -public class StorPoolPrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeCycle { - protected Logger logger = LogManager.getLogger(getClass()); - +public class StorPoolPrimaryDataStoreLifeCycle extends BasePrimaryDataStoreLifeCycleImpl implements PrimaryDataStoreLifeCycle { @Inject protected PrimaryDataStoreHelper dataStoreHelper; @Inject @@ -84,6 +81,8 @@ public class StorPoolPrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeCy private VMTemplateDetailsDao vmTemplateDetailsDao; @Inject private StoragePoolDetailsDao storagePoolDetailsDao; + @Inject + private StoragePoolAndAccessGroupMapDao storagePoolAndAccessGroupMapDao; @Override public DataStore initialize(Map dsInfos) { @@ -212,10 +211,13 @@ public boolean attachZone(DataStore dataStore, ZoneScope scope, HypervisorType h if (hypervisorType != HypervisorType.KVM) { throw new UnsupportedOperationException("Only KVM hypervisors supported!"); } - List kvmHosts = resourceMgr.listAllUpAndEnabledHostsInOneZoneByHypervisor(HypervisorType.KVM, scope.getScopeId()); - for (HostVO host : kvmHosts) { + List kvmHostsToConnect = resourceMgr.getEligibleUpAndEnabledHostsInZoneForStorageConnection(dataStore, scope.getScopeId(), HypervisorType.KVM); + + logger.debug(String.format("In createPool. Attaching the pool to each of the hosts in %s.", kvmHostsToConnect)); + + for (HostVO host : kvmHostsToConnect) { try { - storageMgr.connectHostToSharedPool(host.getId(), dataStore.getId()); + storageMgr.connectHostToSharedPool(host, dataStore.getId()); } catch (Exception e) { logger.warn(String.format("Unable to establish a connection between host %s and pool %s due to %s", host, dataStore, e)); } diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorPoolHostListener.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorPoolHostListener.java index b696990c5336..e27e15e04a82 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorPoolHostListener.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorPoolHostListener.java @@ -114,7 +114,7 @@ public boolean hostConnect(long hostId, long poolId) throws StorageConflictExcep if (!isCurrentVersionSupportsEverythingFromPrevious) { String msg = "The current StorPool driver does not support all functionality from the one before upgrade to CS"; StorPoolUtil.spLog("Storage pool [%s] is not connected to host [%s] because the functionality after the upgrade is not full", - poolId, hostId); + pool, host); alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, pool.getDataCenterId(), pool.getPodId(), msg, msg); return false; } @@ -126,24 +126,23 @@ public boolean hostConnect(long hostId, long poolId) throws StorageConflictExcep boolean isPoolConnectedToTheHost = poolHost != null; if (answer == null) { - StorPoolUtil.spLog("Storage pool [%s] is not connected to the host [%s]", poolVO.getName(), host.getName()); + StorPoolUtil.spLog("Storage pool [%s] is not connected to the host [%s]", poolVO, host); deleteVolumeWhenHostCannotConnectPool(conn, volumeOnPool); removePoolOnHost(poolHost, isPoolConnectedToTheHost); - throw new CloudRuntimeException("Unable to get an answer to the modify storage pool command" + pool.getId()); + throw new CloudRuntimeException(String.format("Unable to get an answer to the modify storage pool command for pool %s", pool)); } if (!answer.getResult()) { - StorPoolUtil.spLog("Storage pool [%s] is not connected to the host [%s]", poolVO.getName(), host.getName()); + StorPoolUtil.spLog("Storage pool [%s] is not connected to the host [%s]", poolVO, host); removePoolOnHost(poolHost, isPoolConnectedToTheHost); if (answer.getDetails() != null && isStorPoolVolumeOrStorageNotExistsOnHost(answer)) { deleteVolumeWhenHostCannotConnectPool(conn, volumeOnPool); return false; } - String msg = "Unable to attach storage pool" + poolId + " to the host" + hostId; + String msg = String.format("Unable to attach storage pool %s to the host %s", pool, host); alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, pool.getDataCenterId(), pool.getPodId(), msg, msg); - throw new CloudRuntimeException("Unable establish connection from storage head to storage pool " + pool.getId() + " due to " + answer.getDetails() + - pool.getId()); + throw new CloudRuntimeException(String.format("Unable establish connection from storage head to storage pool %s due to %s", pool, answer.getDetails())); } StorPoolModifyStoragePoolAnswer mspAnswer = (StorPoolModifyStoragePoolAnswer)answer; @@ -152,9 +151,8 @@ public boolean hostConnect(long hostId, long poolId) throws StorageConflictExcep List localStoragePools = primaryStoreDao.listLocalStoragePoolByPath(pool.getDataCenterId(), datastoreName); for (StoragePoolVO localStoragePool : localStoragePools) { if (datastoreName.equals(localStoragePool.getPath())) { - logger.warn("Storage pool: " + pool.getId() + " has already been added as local storage: " + localStoragePool.getName()); - throw new StorageConflictException("Cannot add shared storage pool: " + pool.getId() + " because it has already been added as local storage:" - + localStoragePool.getName()); + logger.warn("Storage pool: {} has already been added as local storage: {}", pool, localStoragePool); + throw new StorageConflictException(String.format("Cannot add shared storage pool: %s because it has already been added as local storage: %s", pool, localStoragePool)); } } } @@ -172,8 +170,9 @@ public boolean hostConnect(long hostId, long poolId) throws StorageConflictExcep } StorPoolHelper.setSpClusterIdIfNeeded(hostId, mspAnswer.getClusterId(), clusterDao, hostDao, clusterDetailsDao); + StorPoolHelper.setLocationIfNeeded(pool, storagePoolDetailsDao, mspAnswer.getClusterLocation()); - StorPoolUtil.spLog("Connection established between storage pool [%s] and host [%s]", poolVO.getName(), host.getName()); + StorPoolUtil.spLog("Connection established between storage pool [%s] and host [%s]", poolVO, host); return true; } diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolHelper.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolHelper.java index 5a84e699f52e..388211eab49d 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolHelper.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolHelper.java @@ -19,26 +19,6 @@ package org.apache.cloudstack.storage.datastore.util; -import java.sql.PreparedStatement; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.apache.cloudstack.framework.config.dao.ConfigurationDao; -import org.apache.cloudstack.framework.config.impl.ConfigurationVO; -import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; -import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; -import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; -import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; -import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; -import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; -import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpApiResponse; -import org.apache.cloudstack.storage.snapshot.StorPoolConfigurationManager; -import org.apache.cloudstack.storage.to.VolumeObjectTO; -import org.apache.commons.collections4.CollectionUtils; - import com.cloud.dc.ClusterDetailsDao; import com.cloud.dc.ClusterDetailsVO; import com.cloud.dc.ClusterVO; @@ -49,6 +29,7 @@ import com.cloud.server.ResourceTag; import com.cloud.server.ResourceTag.ResourceObjectType; import com.cloud.storage.DataStoreRole; +import com.cloud.storage.StoragePool; import com.cloud.storage.VMTemplateStoragePoolVO; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.SnapshotDetailsDao; @@ -65,6 +46,28 @@ import com.cloud.vm.VMInstanceVO; import com.cloud.vm.dao.VMInstanceDao; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.config.impl.ConfigurationVO; +import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; +import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; +import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; +import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; +import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpApiResponse; +import org.apache.cloudstack.storage.snapshot.StorPoolConfigurationManager; +import org.apache.cloudstack.storage.to.VolumeObjectTO; + +import org.apache.commons.collections4.CollectionUtils; + +import java.sql.PreparedStatement; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; + public class StorPoolHelper { private static final String UPDATE_SNAPSHOT_DETAILS_VALUE = "UPDATE `cloud`.`snapshot_details` SET value=? WHERE id=?"; @@ -102,7 +105,7 @@ public static String getSnapshotName(Long snapshotId, String snapshotUuid, Snaps if (snapshotDetails != null) { return StorPoolStorageAdaptor.getVolumeNameFromPath(snapshotDetails.getValue(), true); } else { - List snapshots = snapshotStoreDao.findBySnapshotId(snapshotId); + List snapshots = snapshotStoreDao.findBySnapshotIdWithNonDestroyedState(snapshotId); if (!CollectionUtils.isEmpty(snapshots)) { for (SnapshotDataStoreVO snapshotDataStoreVO : snapshots) { String name = StorPoolStorageAdaptor.getVolumeNameFromPath(snapshotDataStoreVO.getInstallPath(), true); @@ -163,43 +166,18 @@ public static String getVMInstanceUUID(Long id, VMInstanceDao vmInstanceDao) { return null; } - public static Map addStorPoolTags(String name, String vmUuid, String csTag, String vcPolicy) { + public static Map addStorPoolTags(String name, String vmUuid, String csTag, String vcPolicy, String qcTier) { Map tags = new HashMap<>(); tags.put("uuid", name); tags.put("cvm", vmUuid); tags.put(StorPoolUtil.SP_VC_POLICY, vcPolicy); + tags.put("qc", qcTier); if (csTag != null) { tags.put("cs", csTag); } return tags; } - // Initialize custom logger for updated volume and snapshots -// public static void appendLogger(Logger log, String filePath, String kindOfLog) { -// Appender appender = null; -// PatternLayout patternLayout = new PatternLayout(); -// patternLayout.setConversionPattern("%d{YYYY-MM-dd HH:mm:ss.SSS} %m%n"); -// SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); -// Timestamp timestamp = new Timestamp(System.currentTimeMillis()); -// String path = filePath + "-" + sdf.format(timestamp) + ".log"; -// try { -// appender = new RollingFileAppender(patternLayout, path); -// log.setAdditivity(false); -// log.addAppender(appender); -// } catch (IOException e) { -// e.printStackTrace(); -// } -// if (kindOfLog.equals("update")) { -// StorPoolUtil.spLog( -// "You can find information about volumes and snapshots, which will be updated in Database with their globalIs in %s log file", -// path); -// } else if (kindOfLog.equals("abandon")) { -// StorPoolUtil.spLog( -// "You can find information about volumes and snapshots, for which CloudStack doesn't have information in %s log file", -// path); -// } -// } - public static void setSpClusterIdIfNeeded(long hostId, String clusterId, ClusterDao clusterDao, HostDao hostDao, ClusterDetailsDao clusterDetails) { HostVO host = hostDao.findById(hostId); @@ -217,18 +195,34 @@ public static void setSpClusterIdIfNeeded(long hostId, String clusterId, Cluster } } + public static void setLocationIfNeeded(StoragePool storagePool, StoragePoolDetailsDao storagePoolDetails, + String location) { + if (location == null) { + return; + } + StoragePoolDetailVO storagePoolDetailVO = storagePoolDetails.findDetail(storagePool.getId(), + StorPoolConfigurationManager.StorPoolClusterLocation.key()); + if (storagePoolDetailVO == null) { + storagePoolDetails.persist(new StoragePoolDetailVO(storagePool.getId(), + StorPoolConfigurationManager.StorPoolClusterLocation.key(), location, true)); + } else if (storagePoolDetailVO.getValue() == null || !storagePoolDetailVO.getValue().equals(location)) { + storagePoolDetailVO.setValue(location); + storagePoolDetails.update(storagePoolDetailVO.getId(), storagePoolDetailVO); + } + } + public static Long findClusterIdByGlobalId(String globalId, ClusterDao clusterDao) { - List clusterVo = clusterDao.listAll(); - if (clusterVo.size() == 1) { + List clusterIds = clusterDao.listAllIds(); + if (clusterIds.size() == 1) { StorPoolUtil.spLog("There is only one cluster, sending backup to secondary command"); return null; } - for (ClusterVO clusterVO2 : clusterVo) { - if (globalId != null && StorPoolConfigurationManager.StorPoolClusterId.valueIn(clusterVO2.getId()) != null - && globalId.contains(StorPoolConfigurationManager.StorPoolClusterId.valueIn(clusterVO2.getId()).toString())) { - StorPoolUtil.spLog("Found cluster with id=%s for object with globalId=%s", clusterVO2.getId(), + for (Long clusterId : clusterIds) { + if (globalId != null && StorPoolConfigurationManager.StorPoolClusterId.valueIn(clusterId) != null + && globalId.contains(StorPoolConfigurationManager.StorPoolClusterId.valueIn(clusterId))) { + StorPoolUtil.spLog("Found cluster with id=%s for object with globalId=%s", clusterId, globalId); - return clusterVO2.getId(); + return clusterId; } } throw new CloudRuntimeException( @@ -237,7 +231,7 @@ public static Long findClusterIdByGlobalId(String globalId, ClusterDao clusterDa public static HostVO findHostByCluster(Long clusterId, HostDao hostDao) { List host = hostDao.findByClusterId(clusterId); - return host != null ? host.get(0) : null; + return CollectionUtils.isNotEmpty(host) ? host.get(0) : null; } public static int getTimeout(String cfg, ConfigurationDao configDao) { @@ -288,4 +282,15 @@ public static boolean isPoolSupportsAllFunctionalityFromPreviousVersion(StorageP } return true; } + + public static StorPoolUtil.SpConnectionDesc getSpConnectionDesc(StorPoolUtil.SpConnectionDesc connectionLocal, Long clusterId) { + + String subClusterEndPoint = StorPoolConfigurationManager.StorPoolSubclusterEndpoint.valueIn(clusterId); + if (StringUtils.isNotEmpty(subClusterEndPoint)) { + String host = subClusterEndPoint.split(";")[0].split("=")[1]; + String token = subClusterEndPoint.split(";")[1].split("=")[1]; + connectionLocal = new StorPoolUtil.SpConnectionDesc(host, token, connectionLocal.getTemplateName()); + } + return connectionLocal; + } } diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolUtil.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolUtil.java index 214f93f735fb..dc4dacba450a 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolUtil.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolUtil.java @@ -28,6 +28,9 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; + +import org.apache.cloudstack.storage.datastore.api.StorPoolSnapshotDef; +import org.apache.cloudstack.storage.datastore.api.StorPoolVolumeDef; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; @@ -125,6 +128,21 @@ public static void spLog(String fmt, Object... args) { public static final String SP_VOLUME_ON_CLUSTER = "SP_VOLUME_ON_CLUSTER"; + private static final String DATA = "data"; + + private static final String CLUSTERS = "clusters"; + + public static final String SP_DELAY_DELETE = "SP_DELAY_DELETE"; + + public static final String DELAY_DELETE = "delayDelete"; + + public static final String SP_TIER = "SP_QOSCLASS"; + public static final String SP_RECOVERED_SNAPSHOT = "SP_RECOVERED_SNAPSHOT"; + + public static final String SP_REMOTE_LOCATION = "SP_REMOTE_LOCATION"; + + public static final String OBJECT_DOES_NOT_EXIST = "objectDoesNotExist"; + public static enum StorpoolRights { RO("ro"), RW("rw"), DETACH("detach"); @@ -414,34 +432,46 @@ public static boolean snapshotExists(final String name, SpConnectionDesc conn) { return resp.getError() == null ? true : objectExists(resp.getError()); } + public static boolean snapshotRecovered(final String name, SpConnectionDesc conn) { + SpApiResponse resp = GET("Snapshot/" + name, conn); + JsonObject obj = resp.fullJson.getAsJsonObject(); + JsonObject data = obj.getAsJsonArray("data").get(0).getAsJsonObject(); + boolean recoveringFromRemote = data.getAsJsonPrimitive("recoveringFromRemote").getAsBoolean(); + return recoveringFromRemote; + } + public static JsonArray snapshotsList(SpConnectionDesc conn) { SpApiResponse resp = GET("MultiCluster/SnapshotsList", conn); JsonObject obj = resp.fullJson.getAsJsonObject(); - JsonArray data = obj.getAsJsonArray("data"); - return data; + return obj.getAsJsonArray(DATA); + } + + public static JsonArray snapshotsListAllClusters(SpConnectionDesc conn) { + SpApiResponse resp = GET("MultiCluster/AllClusters/SnapshotsList", conn); + JsonObject obj = resp.fullJson.getAsJsonObject(); + return obj.getAsJsonObject(DATA).getAsJsonArray(CLUSTERS); } public static JsonArray volumesList(SpConnectionDesc conn) { SpApiResponse resp = GET("MultiCluster/VolumesList", conn); JsonObject obj = resp.fullJson.getAsJsonObject(); - JsonArray data = obj.getAsJsonArray("data"); - return data; + return obj.getAsJsonArray(DATA); } public static JsonArray volumesSpace(SpConnectionDesc conn) { SpApiResponse resp = GET("MultiCluster/AllClusters/VolumesSpace", conn); JsonObject obj = resp.fullJson.getAsJsonObject(); - return obj.getAsJsonObject("data").getAsJsonArray("clusters"); + return obj.getAsJsonObject(DATA).getAsJsonArray(CLUSTERS); } public static JsonArray templatesStats(SpConnectionDesc conn) { SpApiResponse resp = GET("MultiCluster/AllClusters/VolumeTemplatesStatus", conn); JsonObject obj = resp.fullJson.getAsJsonObject(); - return obj.getAsJsonObject("data").getAsJsonArray("clusters"); + return obj.getAsJsonObject(DATA).getAsJsonArray(CLUSTERS); } private static boolean objectExists(SpApiError err) { - if (!err.getName().equals("objectDoesNotExist")) { + if (!err.getName().equals(OBJECT_DOES_NOT_EXIST)) { throw new CloudRuntimeException(err.getDescr()); } return false; @@ -454,7 +484,7 @@ public static Long snapshotSize(final String name, SpConnectionDesc conn) { if (resp.getError() != null && !objectExists(resp.getError())) { return null; } - JsonObject data = obj.getAsJsonArray("data").get(0).getAsJsonObject(); + JsonObject data = obj.getAsJsonArray(DATA).get(0).getAsJsonObject(); return data.getAsJsonPrimitive("size").getAsLong(); } @@ -462,7 +492,7 @@ public static String getSnapshotClusterID(String name, SpConnectionDesc conn) { SpApiResponse resp = GET("MultiCluster/Snapshot/" + name, conn); JsonObject obj = resp.fullJson.getAsJsonObject(); - JsonObject data = obj.getAsJsonArray("data").get(0).getAsJsonObject(); + JsonObject data = obj.getAsJsonArray(DATA).get(0).getAsJsonObject(); JsonPrimitive clusterId = data.getAsJsonPrimitive("clusterId"); return clusterId != null ? clusterId.getAsString() : null; } @@ -471,7 +501,7 @@ public static String getVolumeClusterID(String name, SpConnectionDesc conn) { SpApiResponse resp = GET("MultiCluster/Volume/" + name, conn); JsonObject obj = resp.fullJson.getAsJsonObject(); - JsonObject data = obj.getAsJsonArray("data").get(0).getAsJsonObject(); + JsonObject data = obj.getAsJsonArray(DATA).get(0).getAsJsonObject(); JsonPrimitive clusterId = data.getAsJsonPrimitive("clusterId"); return clusterId != null ? clusterId.getAsString() : null; } @@ -484,11 +514,27 @@ public static SpApiResponse volumeCreate(final String name, final String parentN json.put("parent", parentName); json.put("size", size); json.put("template", conn.getTemplateName()); - Map tags = StorPoolHelper.addStorPoolTags(name, vmUuid, csTag, vcPolicy); + Map tags = StorPoolHelper.addStorPoolTags(name, vmUuid, csTag, vcPolicy, null); json.put("tags", tags); return POST("MultiCluster/VolumeCreate", json, conn); } + public static SpApiResponse volumeCreate(Long size, String parentName, String template, Map tags, SpConnectionDesc conn) { + template = template != null ? template : conn.getTemplateName(); + + Map json = new LinkedHashMap<>(); + json.put("name", ""); + json.put("parent", parentName); + json.put("size", size); + json.put("template", template); + json.put("tags", tags); + return POST("MultiCluster/VolumeCreate", json, conn); + } + + public static SpApiResponse volumeCreate(StorPoolVolumeDef volume, SpConnectionDesc conn) { + return POST("MultiCluster/VolumeCreate", volume, conn); + } + public static SpApiResponse volumeCreate(SpConnectionDesc conn) { Map json = new LinkedHashMap<>(); json.put("name", ""); @@ -508,7 +554,7 @@ public static SpApiResponse volumeCopy(final String name, final String baseOn, S json.put("iops", iops); } json.put("template", conn.getTemplateName()); - Map tags = StorPoolHelper.addStorPoolTags(name, cvmTag, csTag, vcPolicyTag); + Map tags = StorPoolHelper.addStorPoolTags(name, cvmTag, csTag, vcPolicyTag, null); json.put("tags", tags); return POST("MultiCluster/VolumeCreate", json, conn); } @@ -536,7 +582,8 @@ public static SpApiResponse volumeUpdate(final String name, final Long newSize, public static SpApiResponse volumeRemoveTags(String name, SpConnectionDesc conn) { Map json = new HashMap<>(); - Map tags = StorPoolHelper.addStorPoolTags(null, "", null, ""); + Map tags = StorPoolHelper.addStorPoolTags(null, "", null, "", null); + tags.put("disk", ""); json.put("tags", tags); return POST("MultiCluster/VolumeUpdate/" + name, json, conn); } @@ -544,7 +591,7 @@ public static SpApiResponse volumeRemoveTags(String name, SpConnectionDesc conn) public static SpApiResponse volumeUpdateIopsAndTags(final String name, final String uuid, Long iops, SpConnectionDesc conn, String vcPolicy) { Map json = new HashMap<>(); - Map tags = StorPoolHelper.addStorPoolTags(null, uuid, null, vcPolicy); + Map tags = StorPoolHelper.addStorPoolTags(null, uuid, null, vcPolicy, null); json.put("iops", iops); json.put("tags", tags); return POST("MultiCluster/VolumeUpdate/" + name, json, conn); @@ -552,14 +599,14 @@ public static SpApiResponse volumeUpdateIopsAndTags(final String name, final Str public static SpApiResponse volumeUpdateCvmTags(final String name, final String uuid, SpConnectionDesc conn) { Map json = new HashMap<>(); - Map tags = StorPoolHelper.addStorPoolTags(null, uuid, null, null); + Map tags = StorPoolHelper.addStorPoolTags(null, uuid, null, null, null); json.put("tags", tags); return POST("MultiCluster/VolumeUpdate/" + name, json, conn); } public static SpApiResponse volumeUpdateVCTags(final String name, SpConnectionDesc conn, String vcPolicy) { Map json = new HashMap<>(); - Map tags = StorPoolHelper.addStorPoolTags(null, null, null, vcPolicy); + Map tags = StorPoolHelper.addStorPoolTags(null, null, null, vcPolicy, null); json.put("tags", tags); return POST("MultiCluster/VolumeUpdate/" + name, json, conn); } @@ -570,20 +617,28 @@ public static SpApiResponse volumeUpdateTemplate(final String name, SpConnection return POST("MultiCluster/VolumeUpdate/" + name, json, conn); } + public static SpApiResponse volumeUpdate(StorPoolVolumeDef volume, SpConnectionDesc conn) { + return POST("MultiCluster/VolumeUpdate/" + volume.getName(), volume, conn); + } + public static SpApiResponse volumeSnapshot(final String volumeName, final String snapshotName, String vmUuid, String csTag, String vcPolicy, SpConnectionDesc conn) { Map json = new HashMap<>(); - Map tags = StorPoolHelper.addStorPoolTags(snapshotName, vmUuid, csTag, vcPolicy); + Map tags = StorPoolHelper.addStorPoolTags(snapshotName, vmUuid, csTag, vcPolicy, null); json.put("name", ""); json.put("tags", tags); return POST("MultiCluster/VolumeSnapshot/" + volumeName, json, conn); } + public static SpApiResponse volumeSnapshot(StorPoolSnapshotDef snapshot, SpConnectionDesc conn) { + return POST("MultiCluster/VolumeSnapshot/" + snapshot.getVolumeName(), snapshot, conn); + } + public static SpApiResponse volumesGroupSnapshot(final List volumeTOs, final String vmUuid, final String snapshotName, String csTag, SpConnectionDesc conn) { Map json = new LinkedHashMap<>(); - Map tags = StorPoolHelper.addStorPoolTags(snapshotName, vmUuid, csTag, null); + Map tags = StorPoolHelper.addStorPoolTags(snapshotName, vmUuid, csTag, null, null); List> volumes = new ArrayList<>(); for (VolumeObjectTO volumeTO : volumeTOs) { Map vol = new LinkedHashMap<>(); @@ -604,6 +659,12 @@ public static SpApiResponse volumeRevert(final String name, final String snapsho return POST("MultiCluster/VolumeRevert/" + name, json, conn); } + /** + * @deprecated Use volumeSnapshot instead + * @param volumeName + * @param conn + * @return + */ public static SpApiResponse volumeFreeze(final String volumeName, SpConnectionDesc conn) { return POST("MultiCluster/VolumeFreeze/" + volumeName, null, conn); } @@ -625,6 +686,42 @@ public static SpApiResponse snapshotDelete(final String name, SpConnectionDesc c return resp.getError() == null ? POST("MultiCluster/SnapshotDelete/" + name, null, conn) : resp; } + public static SpApiResponse snapshotExport(String name, String location, SpConnectionDesc conn) { + Map json = new HashMap<>(); + json.put("snapshot", name); + json.put("location", location); + return POST("SnapshotExport", json, conn); + } + + public static SpApiResponse snapshotUnexport(String name, String location, SpConnectionDesc conn) { + Map json = new HashMap<>(); + json.put("snapshot", name); + json.put("force", true); + json.put("all", true); + return POST("SnapshotUnexport", json, conn); + } + + public static String getSnapshotClusterId(String snapshotName, SpConnectionDesc conn) { + SpApiResponse resp = POST("MultiCluster/SnapshotUpdate/" + snapshotName, new HashMap<>(), conn); + JsonObject json = resp.fullJson.getAsJsonObject(); + return json.get("clusterId").getAsString(); + } + + public static SpApiResponse snapshotFromRemote(String name, String remoteLocation, String template, Map tags, + SpConnectionDesc conn) { + Map json = new HashMap<>(); + json.put("remoteId", name); + json.put("remoteLocation", remoteLocation); + json.put("template", template); + json.put("name", ""); + json.put("tags", tags); + return POST("SnapshotFromRemote", json, conn); + } + + public static SpApiResponse snapshotReconcile(String name, SpConnectionDesc conn) { + return POST("SnapshotReconcile/" + name, null, conn); + } + public static SpApiResponse detachAllForced(final String name, final boolean snapshot, SpConnectionDesc conn) { final String type = snapshot ? "snapshot" : "volume"; List> json = new ArrayList<>(); @@ -639,7 +736,7 @@ public static SpApiResponse detachAllForced(final String name, final boolean sna public static String getSnapshotNameFromResponse(SpApiResponse resp, boolean tildeNeeded, String globalIdOrRemote) { JsonObject obj = resp.fullJson.getAsJsonObject(); - JsonPrimitive data = obj.getAsJsonObject("data").getAsJsonPrimitive(globalIdOrRemote); + JsonPrimitive data = obj.getAsJsonObject(DATA).getAsJsonPrimitive(globalIdOrRemote); String name = data != null ? data.getAsString() : null; name = name != null ? !tildeNeeded ? name : "~" + name : name; return name; @@ -647,7 +744,7 @@ public static String getSnapshotNameFromResponse(SpApiResponse resp, boolean til public static String getNameFromResponse(SpApiResponse resp, boolean tildeNeeded) { JsonObject obj = resp.fullJson.getAsJsonObject(); - JsonPrimitive data = obj.getAsJsonObject("data").getAsJsonPrimitive("name"); + JsonPrimitive data = obj.getAsJsonObject(DATA).getAsJsonPrimitive("name"); String name = data != null ? data.getAsString() : null; name = name != null ? name.startsWith("~") && !tildeNeeded ? name.split("~")[1] : name : name; return name; diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/motion/StorPoolDataMotionStrategy.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/motion/StorPoolDataMotionStrategy.java index bd5380cc1606..c231290bc7cf 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/motion/StorPoolDataMotionStrategy.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/motion/StorPoolDataMotionStrategy.java @@ -19,12 +19,42 @@ package org.apache.cloudstack.storage.motion; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.inject.Inject; +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.MigrateAnswer; +import com.cloud.agent.api.MigrateCommand; +import com.cloud.agent.api.MigrateCommand.MigrateDiskInfo; +import com.cloud.agent.api.ModifyTargetsAnswer; +import com.cloud.agent.api.ModifyTargetsCommand; +import com.cloud.agent.api.PrepareForMigrationCommand; +import com.cloud.agent.api.storage.StorPoolBackupTemplateFromSnapshotCommand; +import com.cloud.agent.api.to.DataObjectType; +import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.dc.dao.ClusterDao; +import com.cloud.exception.AgentUnavailableException; +import com.cloud.exception.OperationTimedoutException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.storage.DataStoreRole; +import com.cloud.storage.Storage.ImageFormat; +import com.cloud.storage.StorageManager; +import com.cloud.storage.VMTemplateDetailVO; +import com.cloud.storage.Volume; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.GuestOSCategoryDao; +import com.cloud.storage.dao.GuestOSDao; +import com.cloud.storage.dao.SnapshotDao; +import com.cloud.storage.dao.SnapshotDetailsDao; +import com.cloud.storage.dao.SnapshotDetailsVO; +import com.cloud.storage.dao.VMTemplateDetailsDao; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachineManager; +import com.cloud.vm.dao.VMInstanceDao; import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy; @@ -55,48 +85,21 @@ import org.apache.cloudstack.storage.datastore.util.StorPoolUtil; import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpApiResponse; import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpConnectionDesc; -import org.apache.cloudstack.storage.snapshot.StorPoolConfigurationManager; import org.apache.cloudstack.storage.to.SnapshotObjectTO; import org.apache.cloudstack.storage.to.TemplateObjectTO; + import org.apache.commons.collections.MapUtils; -import org.apache.logging.log4j.Logger; + import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import org.springframework.stereotype.Component; -import com.cloud.agent.AgentManager; -import com.cloud.agent.api.Answer; -import com.cloud.agent.api.Command; -import com.cloud.agent.api.MigrateAnswer; -import com.cloud.agent.api.MigrateCommand; -import com.cloud.agent.api.MigrateCommand.MigrateDiskInfo; -import com.cloud.agent.api.ModifyTargetsAnswer; -import com.cloud.agent.api.ModifyTargetsCommand; -import com.cloud.agent.api.PrepareForMigrationCommand; -import com.cloud.agent.api.storage.StorPoolBackupTemplateFromSnapshotCommand; -import com.cloud.agent.api.to.DataObjectType; -import com.cloud.agent.api.to.VirtualMachineTO; -import com.cloud.dc.dao.ClusterDao; -import com.cloud.exception.AgentUnavailableException; -import com.cloud.exception.OperationTimedoutException; -import com.cloud.host.Host; -import com.cloud.host.dao.HostDao; -import com.cloud.hypervisor.Hypervisor.HypervisorType; -import com.cloud.storage.Storage.ImageFormat; -import com.cloud.storage.StorageManager; -import com.cloud.storage.VMTemplateDetailVO; -import com.cloud.storage.Volume; -import com.cloud.storage.VolumeVO; -import com.cloud.storage.dao.GuestOSCategoryDao; -import com.cloud.storage.dao.GuestOSDao; -import com.cloud.storage.dao.SnapshotDao; -import com.cloud.storage.dao.SnapshotDetailsDao; -import com.cloud.storage.dao.SnapshotDetailsVO; -import com.cloud.storage.dao.VMTemplateDetailsDao; -import com.cloud.storage.dao.VolumeDao; -import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.vm.VMInstanceVO; -import com.cloud.vm.VirtualMachineManager; -import com.cloud.vm.dao.VMInstanceDao; +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; @Component public class StorPoolDataMotionStrategy implements DataMotionStrategy { @@ -149,14 +152,20 @@ public class StorPoolDataMotionStrategy implements DataMotionStrategy { public StrategyPriority canHandle(DataObject srcData, DataObject destData) { DataObjectType srcType = srcData.getType(); DataObjectType dstType = destData.getType(); - if (srcType == DataObjectType.SNAPSHOT && dstType == DataObjectType.TEMPLATE - && StorPoolConfigurationManager.BypassSecondaryStorage.value()) { + + if (srcType == DataObjectType.SNAPSHOT && dstType == DataObjectType.TEMPLATE) { SnapshotInfo sinfo = (SnapshotInfo) srcData; - VolumeInfo volume = sinfo.getBaseVolume(); - StoragePoolVO storagePool = _storagePool.findById(volume.getPoolId()); + if (!sinfo.getDataStore().getRole().equals(DataStoreRole.Primary)) { + return StrategyPriority.CANT_HANDLE; + } + StoragePoolVO storagePool = _storagePool.findById(sinfo.getDataStore().getId()); if (!storagePool.getStorageProviderName().equals(StorPoolUtil.SP_PROVIDER_NAME)) { return StrategyPriority.CANT_HANDLE; } + SnapshotDetailsVO snapshotDetail = _snapshotDetailsDao.findDetail(sinfo.getId(), StorPoolUtil.SP_DELAY_DELETE); + if (snapshotDetail != null) { + throw new CloudRuntimeException("Cannot create a template from the last snapshot of deleted volume. You can only restore the volume."); + } String snapshotName = StorPoolHelper.getSnapshotName(sinfo.getId(), sinfo.getUuid(), _snapshotStoreDao, _snapshotDetailsDao); StorPoolUtil.spLog("StorPoolDataMotionStrategy.canHandle snapshot name=%s", snapshotName); @@ -172,13 +181,12 @@ public void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback callback) { SnapshotObjectTO snapshot = (SnapshotObjectTO) srcData.getTO(); TemplateObjectTO template = (TemplateObjectTO) destData.getTO(); - DataStore store = _dataStore.getDataStore(snapshot.getVolume().getDataStore().getUuid(), - snapshot.getVolume().getDataStore().getRole()); + DataStore store = _dataStore.getDataStore(snapshot.getDataStore().getUuid(), + snapshot.getDataStore().getRole()); SnapshotInfo sInfo = _snapshotDataFactory.getSnapshot(snapshot.getId(), store); - VolumeInfo vInfo = sInfo.getBaseVolume(); - SpConnectionDesc conn = StorPoolUtil.getSpConnection(vInfo.getDataStore().getUuid(), - vInfo.getDataStore().getId(), _storagePoolDetails, _storagePool); + SpConnectionDesc conn = StorPoolUtil.getSpConnection(sInfo.getDataStore().getUuid(), + sInfo.getDataStore().getId(), _storagePoolDetails, _storagePool); String name = template.getUuid(); String volumeName = ""; @@ -190,7 +198,7 @@ public void copyAsync(DataObject srcData, DataObject destData, Host destHost, CopyCmdAnswer answer = null; String err = null; if (res.getError() != null) { - logger.debug(String.format("Could not create volume from snapshot with ID=%s", snapshot.getId())); + logger.debug("Could not create volume from snapshot [ID: {}, name: {}]", snapshot.getId(), snapshot.getName()); StorPoolUtil.spLog("Volume create failed with error=%s", res.getError().getDescr()); err = res.getError().getDescr(); } else { @@ -206,37 +214,34 @@ public void copyAsync(DataObject srcData, DataObject destData, Host destHost, // final String snapName = // StorpoolStorageAdaptor.getVolumeNameFromPath(((SnapshotInfo) // srcData).getPath(), true); - Long clusterId = StorPoolHelper.findClusterIdByGlobalId(parentName, _clusterDao); - EndPoint ep2 = clusterId != null - ? RemoteHostEndPoint - .getHypervisorHostEndPoint(StorPoolHelper.findHostByCluster(clusterId, _hostDao)) - : _selector.select(sInfo, destData); + Long clusterId = StorPoolHelper.findClusterIdByGlobalId(StorPoolUtil.getSnapshotClusterId(parentName, conn), _clusterDao); + HostVO host = clusterId != null ? StorPoolHelper.findHostByCluster(clusterId, _hostDao) : null; + EndPoint ep2 = host != null ? RemoteHostEndPoint.getHypervisorHostEndPoint(host) : _selector.select(srcData, destData); if (ep2 == null) { err = "No remote endpoint to send command, check if host or ssvm is down?"; } else { answer = (CopyCmdAnswer) ep2.sendMessage(backupSnapshot); if (answer != null && answer.getResult()) { - SpApiResponse resSnapshot = StorPoolUtil.volumeFreeze(volumeName, conn); + SpApiResponse resSnapshot = StorPoolUtil.volumeSnapshot(volumeName, template.getUuid(), null, "template", null, conn); if (resSnapshot.getError() != null) { - logger.debug(String.format("Could not snapshot volume with ID=%s", snapshot.getId())); - StorPoolUtil.spLog("Volume freeze failed with error=%s", resSnapshot.getError().getDescr()); + logger.debug(String.format("Could not snapshot volume with ID={}", snapshot.getId())); + StorPoolUtil.spLog("VolumeSnapshot failed with error=%s", resSnapshot.getError().getDescr()); err = resSnapshot.getError().getDescr(); - StorPoolUtil.volumeDelete(volumeName, conn); } else { + String templPath = StorPoolUtil.devPath( + StorPoolUtil.getSnapshotNameFromResponse(resSnapshot, false, StorPoolUtil.GLOBAL_ID)); StorPoolHelper.updateVmStoreTemplate(template.getId(), template.getDataStore().getRole(), - StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(res, false)), _templStoreDao); + templPath, _templStoreDao); } - } else { - err = "Could not copy template to secondary " + answer.getResult(); - StorPoolUtil.volumeDelete(StorPoolUtil.getNameFromResponse(res, true), conn); } } } catch (CloudRuntimeException e) { err = e.getMessage(); } + StorPoolUtil.volumeDelete(volumeName, conn); } _vmTemplateDetailsDao.persist(new VMTemplateDetailVO(template.getId(), StorPoolUtil.SP_STORAGE_POOL_ID, - String.valueOf(vInfo.getDataStore().getId()), false)); + String.valueOf(sInfo.getDataStore().getId()), false)); StorPoolUtil.spLog("StorPoolDataMotionStrategy.copyAsync Creating snapshot=%s for StorPool template=%s", volumeName, conn.getTemplateName()); final CopyCommandResult cmd = new CopyCommandResult(null, answer); @@ -294,7 +299,7 @@ public void copyAsync(Map volumeDataStoreMap, VirtualMach for (Map.Entry entry : volumeDataStoreMap.entrySet()) { VolumeInfo srcVolumeInfo = entry.getKey(); if (srcVolumeInfo.getPassphraseId() != null) { - throw new CloudRuntimeException(String.format("Cannot live migrate encrypted volume [%s] to StorPool", srcVolumeInfo.getName())); + throw new CloudRuntimeException(String.format("Cannot live migrate encrypted volume [%s] to StorPool", srcVolumeInfo.getVolume())); } DataStore destDataStore = entry.getValue(); @@ -385,7 +390,7 @@ public void copyAsync(Map volumeDataStoreMap, VirtualMach errMsg = String.format( "Copy volume(s) of VM [%s] to storage(s) [%s] and VM to host [%s] failed in StorPoolDataMotionStrategy.copyAsync. Error message: [%s].", - vmTO.getId(), srcHost.getId(), destHost.getId(), ex.getMessage()); + vmTO, srcHost, destHost, ex.getMessage()); logger.error(errMsg, ex); throw new CloudRuntimeException(errMsg); @@ -420,6 +425,7 @@ private VolumeVO duplicateVolumeOnAnotherStorage(Volume volume, StoragePoolVO st newVol.setFolder(null); newVol.setPodId(storagePoolVO.getPodId()); newVol.setPoolId(storagePoolVO.getId()); + newVol.setPoolType(storagePoolVO.getPoolType()); newVol.setLastPoolId(lastPoolId); return _volumeDao.persist(newVol); @@ -451,8 +457,8 @@ private void handlePostMigration(boolean success, Map sr VolumeInfo destVolumeInfo = entry.getValue(); if (success) { - srcVolumeInfo.processEvent(Event.OperationSuccessed); - destVolumeInfo.processEvent(Event.OperationSuccessed); + srcVolumeInfo.processEvent(Event.OperationSucceeded); + destVolumeInfo.processEvent(Event.OperationSucceeded); _volumeDao.updateUuid(srcVolumeInfo.getId(), destVolumeInfo.getId()); @@ -521,13 +527,13 @@ private String generateDestPath(Host destHost, StoragePoolVO destStoragePool, Vo private String connectHostToVolume(Host host, long storagePoolId, String iqn) { ModifyTargetsCommand modifyTargetsCommand = getModifyTargetsCommand(storagePoolId, iqn, true); - return sendModifyTargetsCommand(modifyTargetsCommand, host.getId()).get(0); + return sendModifyTargetsCommand(modifyTargetsCommand, host).get(0); } private void disconnectHostFromVolume(Host host, long storagePoolId, String iqn) { ModifyTargetsCommand modifyTargetsCommand = getModifyTargetsCommand(storagePoolId, iqn, false); - sendModifyTargetsCommand(modifyTargetsCommand, host.getId()); + sendModifyTargetsCommand(modifyTargetsCommand, host); } private ModifyTargetsCommand getModifyTargetsCommand(long storagePoolId, String iqn, boolean add) { @@ -555,15 +561,15 @@ private ModifyTargetsCommand getModifyTargetsCommand(long storagePoolId, String return cmd; } - private List sendModifyTargetsCommand(ModifyTargetsCommand cmd, long hostId) { - ModifyTargetsAnswer modifyTargetsAnswer = (ModifyTargetsAnswer) _agentManager.easySend(hostId, cmd); + private List sendModifyTargetsCommand(ModifyTargetsCommand cmd, Host host) { + ModifyTargetsAnswer modifyTargetsAnswer = (ModifyTargetsAnswer) _agentManager.easySend(host.getId(), cmd); if (modifyTargetsAnswer == null) { throw new CloudRuntimeException("Unable to get an answer to the modify targets command"); } if (!modifyTargetsAnswer.getResult()) { - String msg = "Unable to modify targets on the following host: " + hostId; + String msg = String.format("Unable to modify targets on the following host: %s", host); throw new CloudRuntimeException(msg); } diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolConfigurationManager.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolConfigurationManager.java index dcb2b226467a..00cef88c4cfc 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolConfigurationManager.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolConfigurationManager.java @@ -44,6 +44,20 @@ public class StorPoolConfigurationManager implements Configurable { "The interval in seconds to get StorPool template statistics", false); + public static final ConfigKey DeleteAfterInterval = new ConfigKey<>("Advanced", Integer.class, + "storpool.delete.after.interval", "0", + "The interval (in seconds) after the StorPool snapshot will be deleted", + false, ConfigKey.Scope.StoragePool); + + public static final ConfigKey ListSnapshotsWithDeleteAfterInterval = new ConfigKey<>("Advanced", Integer.class, + "storpool.list.snapshots.delete.after.interval", "360", + "The interval (in seconds) to fetch the StorPool snapshots with deleteAfter flag", + false); + public static final ConfigKey StorPoolClusterLocation = new ConfigKey(String.class, "sp.cluster.location", "Advanced", null, + "StorPool cluster location", true, ConfigKey.Scope.StoragePool, null); + public static final ConfigKey StorPoolSubclusterEndpoint = new ConfigKey<>(String.class, "sp.cluster.endpoint", "Advanced", null, + "StorPool sub-cluster endpoint", true, ConfigKey.Scope.Cluster, null); + @Override public String getConfigComponentName() { return StorPoolConfigurationManager.class.getSimpleName(); @@ -51,6 +65,6 @@ public String getConfigComponentName() { @Override public ConfigKey[] getConfigKeys() { - return new ConfigKey[] { BypassSecondaryStorage, StorPoolClusterId, AlternativeEndPointEnabled, AlternativeEndpoint, VolumesStatsInterval, StorageStatsInterval }; + return new ConfigKey[] { BypassSecondaryStorage, StorPoolClusterId, AlternativeEndPointEnabled, AlternativeEndpoint, VolumesStatsInterval, StorageStatsInterval, DeleteAfterInterval, ListSnapshotsWithDeleteAfterInterval, StorPoolClusterLocation, StorPoolSubclusterEndpoint }; } } diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolSnapshotStrategy.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolSnapshotStrategy.java index 0b58247c661c..a49cf78f79de 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolSnapshotStrategy.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolSnapshotStrategy.java @@ -16,13 +16,30 @@ // under the License. package org.apache.cloudstack.storage.snapshot; -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; +import com.cloud.api.query.dao.SnapshotJoinDao; +import com.cloud.api.query.vo.SnapshotJoinVO; +import com.cloud.dc.dao.ClusterDao; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor; +import com.cloud.storage.DataStoreRole; +import com.cloud.storage.Snapshot; +import com.cloud.storage.SnapshotVO; +import com.cloud.storage.Storage; +import com.cloud.storage.dao.SnapshotDao; +import com.cloud.storage.dao.SnapshotDetailsDao; +import com.cloud.storage.dao.SnapshotDetailsVO; +import com.cloud.storage.dao.SnapshotZoneDao; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.fsm.NoTransitionException; +import java.util.HashMap; +import java.util.Map; +import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult; +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.DataStoreManager; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory; @@ -30,33 +47,30 @@ import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotService; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotStrategy; import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority; +import org.apache.cloudstack.framework.async.AsyncCompletionCallback; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; +import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.util.StorPoolHelper; import org.apache.cloudstack.storage.datastore.util.StorPoolUtil; import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpApiResponse; import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpConnectionDesc; +import org.apache.cloudstack.storage.to.SnapshotObjectTO; + import org.apache.commons.collections.CollectionUtils; -import org.apache.logging.log4j.Logger; + import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import org.jetbrains.annotations.NotNull; import org.springframework.stereotype.Component; -import com.cloud.exception.InvalidParameterValueException; -import com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor; -import com.cloud.storage.DataStoreRole; -import com.cloud.storage.Snapshot; -import com.cloud.storage.SnapshotVO; -import com.cloud.storage.VolumeVO; -import com.cloud.storage.dao.SnapshotDao; -import com.cloud.storage.dao.SnapshotDetailsDao; -import com.cloud.storage.dao.SnapshotDetailsVO; -import com.cloud.storage.dao.SnapshotZoneDao; -import com.cloud.storage.dao.VolumeDao; -import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.utils.fsm.NoTransitionException; +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; @Component @@ -83,6 +97,10 @@ public class StorPoolSnapshotStrategy implements SnapshotStrategy { DataStoreManager dataStoreMgr; @Inject SnapshotZoneDao snapshotZoneDao; + @Inject + SnapshotJoinDao snapshotJoinDao; + @Inject + private ClusterDao clusterDao; @Override public SnapshotInfo backupSnapshot(SnapshotInfo snapshotInfo) { @@ -105,83 +123,145 @@ public SnapshotInfo backupSnapshot(SnapshotInfo snapshotInfo) { public boolean deleteSnapshot(Long snapshotId, Long zoneId) { final SnapshotVO snapshotVO = _snapshotDao.findById(snapshotId); - VolumeVO volume = _volumeDao.findByIdIncludingRemoved(snapshotVO.getVolumeId()); String name = StorPoolHelper.getSnapshotName(snapshotId, snapshotVO.getUuid(), _snapshotStoreDao, _snapshotDetailsDao); boolean res = false; // clean-up snapshot from Storpool storage pools - StoragePoolVO storage = _primaryDataStoreDao.findById(volume.getPoolId()); - if (storage.getStorageProviderName().equals(StorPoolUtil.SP_PROVIDER_NAME)) { - try { - SpConnectionDesc conn = StorPoolUtil.getSpConnection(storage.getUuid(), storage.getId(), storagePoolDetailsDao, _primaryDataStoreDao); - SpApiResponse resp = StorPoolUtil.snapshotDelete(name, conn); - if (resp.getError() != null) { - final String err = String.format("Failed to clean-up Storpool snapshot %s. Error: %s", name, resp.getError()); - StorPoolUtil.spLog(err); - } else { - res = deleteSnapshotFromDbIfNeeded(snapshotVO, zoneId); - StorPoolUtil.spLog("StorpoolSnapshotStrategy.deleteSnapshot: executed successfully=%s, snapshot uuid=%s, name=%s", res, snapshotVO.getUuid(), name); + List snapshotDataStoreVOS; + List snapshotJoinVOList = snapshotJoinDao.listBySnapshotIdAndZoneId(zoneId, snapshotId); + try { + for (SnapshotJoinVO snapshot: snapshotJoinVOList) { + if (State.Destroyed.equals(snapshot.getStatus())) { + continue; + } + if (snapshot.getStoreRole().isImageStore()) { + continue; + } + StoragePoolVO storage = _primaryDataStoreDao.findById(snapshot.getStoreId()); + if (zoneId != null) { + if (!zoneId.equals(snapshot.getDataCenterId())) { + continue; + } + res = deleteSnapshot(snapshotId, zoneId, snapshotVO, name, storage); + break; } - } catch (Exception e) { - String errMsg = String.format("Cannot delete snapshot due to %s", e.getMessage()); - throw new CloudRuntimeException(errMsg); + res = deleteSnapshot(snapshotId, zoneId, snapshotVO, name, storage); } + } catch (Exception e) { + String errMsg = String.format("Cannot delete snapshot due to %s", e.getMessage()); + throw new CloudRuntimeException(errMsg); } + snapshotDataStoreVOS = _snapshotStoreDao.listSnapshotsBySnapshotId(snapshotId); + boolean areAllSnapshotsDestroyed = snapshotDataStoreVOS.stream().allMatch(v -> v.getState().equals(State.Destroyed) || v.getState().equals(State.Destroying)); + if (areAllSnapshotsDestroyed) { + updateSnapshotToDestroyed(snapshotVO); + return true; + } + return res; + } + private boolean deleteSnapshot(Long snapshotId, Long zoneId, SnapshotVO snapshotVO, String name, StoragePoolVO storage) { + + boolean res = false; + SpConnectionDesc conn = StorPoolUtil.getSpConnection(storage.getUuid(), storage.getId(), storagePoolDetailsDao, _primaryDataStoreDao); + SpApiResponse resp = StorPoolUtil.snapshotDelete(name, conn); + List snapshotInfos = snapshotDataFactory.getSnapshots(snapshotId, zoneId); + processResult(snapshotInfos, ObjectInDataStoreStateMachine.Event.DestroyRequested); + if (resp.getError() != null) { + if (resp.getError().getDescr().contains("still exported")) { + processResult(snapshotInfos, Event.OperationFailed); + throw new CloudRuntimeException(String.format("The snapshot [%s] was exported to another cluster. [%s]", name, resp.getError())); + } + final String err = String.format("Failed to clean-up Storpool snapshot %s. Error: %s", name, resp.getError()); + StorPoolUtil.spLog(err); + if (resp.getError().getName().equals("objectDoesNotExist")) { + return true; + } + } else { + res = deleteSnapshotFromDbIfNeeded(snapshotVO, zoneId); + StorPoolUtil.spLog("StorpoolSnapshotStrategy.deleteSnapshot: executed successfully=%s, snapshot uuid=%s, name=%s", res, snapshotVO.getUuid(), name); + } + if (res) { + processResult(snapshotInfos, Event.OperationSucceeded); + cleanUpDestroyedRecords(snapshotId); + } else { + processResult(snapshotInfos, Event.OperationFailed); + } return res; } + private void cleanUpDestroyedRecords(Long snapshotId) { + List snapshots = _snapshotStoreDao.listBySnapshotId(snapshotId); + for (SnapshotDataStoreVO snapshot : snapshots) { + if (snapshot.getInstallPath().contains("/dev/storpool-byid") && State.Destroyed.equals(snapshot.getState())) { + _snapshotStoreDao.remove(snapshot.getId()); + } + } + } + + private void processResult(List snapshotInfos, ObjectInDataStoreStateMachine.Event event) { + for (SnapshotInfo snapshot : snapshotInfos) { + SnapshotObject snapshotObject = (SnapshotObject) snapshot; + if (DataStoreRole.Primary.equals(snapshotObject.getDataStore().getRole())) { + snapshotObject.processEvent(event); + } + } + } + @Override public StrategyPriority canHandle(Snapshot snapshot, Long zoneId, SnapshotOperation op) { - logger.debug(String.format("StorpoolSnapshotStrategy.canHandle: snapshot=%s, uuid=%s, op=%s", snapshot.getName(), snapshot.getUuid(), op)); + logger.debug("StorpoolSnapshotStrategy.canHandle: snapshot {}, op={}", snapshot, op); - if (op != SnapshotOperation.DELETE) { + if (op != SnapshotOperation.DELETE && op != SnapshotOperation.COPY) { return StrategyPriority.CANT_HANDLE; } - SnapshotDataStoreVO snapshotOnPrimary = _snapshotStoreDao.findOneBySnapshotAndDatastoreRole(snapshot.getId(), DataStoreRole.Primary); - if (snapshotOnPrimary == null) { + List pools = _primaryDataStoreDao.findPoolsByStorageType(Storage.StoragePoolType.StorPool); + if (CollectionUtils.isEmpty(pools)) { return StrategyPriority.CANT_HANDLE; } - if (zoneId != null) { // If zoneId is present, then it should be same as the zoneId of primary store - StoragePoolVO storagePoolVO = _primaryDataStoreDao.findById(snapshotOnPrimary.getDataStoreId()); - if (!zoneId.equals(storagePoolVO.getDataCenterId())) { - return StrategyPriority.CANT_HANDLE; - } - } - String name = StorPoolHelper.getSnapshotName(snapshot.getId(), snapshot.getUuid(), _snapshotStoreDao, _snapshotDetailsDao); - if (name != null) { - StorPoolUtil.spLog("StorpoolSnapshotStrategy.canHandle: globalId=%s", name); + List snapshots = snapshotJoinDao.listBySnapshotIdAndZoneId(zoneId, snapshot.getId()); + boolean snapshotNotOnStorPool = snapshots.stream().filter(s -> DataStoreRole.Primary.equals(s.getStoreRole())).count() == 0; - return StrategyPriority.HIGHEST; + if (snapshotNotOnStorPool) { + for (SnapshotJoinVO snapshotOnStore : snapshots) { + SnapshotDataStoreVO snap = _snapshotStoreDao.findOneBySnapshotAndDatastoreRole(snapshot.getId(), DataStoreRole.Image); + if (snap != null && snap.getInstallPath() != null && snap.getInstallPath().startsWith(StorPoolUtil.SP_DEV_PATH)) { + return StrategyPriority.HIGHEST; + } + } + return StrategyPriority.CANT_HANDLE; } - SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(snapshot.getId(), snapshot.getUuid()); - if (snapshotDetails != null) { - _snapshotDetailsDao.remove(snapshotDetails.getId()); + for (StoragePoolVO pool : pools) { + SnapshotDataStoreVO snapshotOnPrimary = _snapshotStoreDao.findByStoreSnapshot(DataStoreRole.Primary, pool.getId(), snapshot.getId()); + if (snapshotOnPrimary != null && (snapshotOnPrimary.getState().equals(State.Ready) || snapshotOnPrimary.getState().equals(State.Created))) { + return StrategyPriority.HIGHEST; + } } + return StrategyPriority.CANT_HANDLE; } private boolean deleteSnapshotChain(SnapshotInfo snapshot) { - logger.debug("delete snapshot chain for snapshot: " + snapshot.getId()); + logger.debug("delete snapshot chain for snapshot: {}", snapshot); final SnapshotInfo snapOnImage = snapshot; boolean result = false; boolean resultIsSet = false; try { while (snapshot != null && - (snapshot.getState() == Snapshot.State.Destroying || snapshot.getState() == Snapshot.State.Destroyed || snapshot.getState() == Snapshot.State.Error)) { + (snapshot.getState() == Snapshot.State.Destroying || snapshot.getState() == Snapshot.State.Destroyed || snapshot.getState() == Snapshot.State.Error || snapshot.getState() == Snapshot.State.BackedUp)) { SnapshotInfo child = snapshot.getChild(); if (child != null) { logger.debug("the snapshot has child, can't delete it on the storage"); break; } - logger.debug("Snapshot: " + snapshot.getId() + " doesn't have children, so it's ok to delete it and its parents"); + logger.debug("Snapshot: {} doesn't have children, so it's ok to delete it and its parents", snapshot); SnapshotInfo parent = snapshot.getParent(); boolean deleted = false; if (parent != null) { if (parent.getPath() != null && parent.getPath().equalsIgnoreCase(snapshot.getPath())) { logger.debug("for empty delta snapshot, only mark it as destroyed in db"); snapshot.processEvent(Event.DestroyRequested); - snapshot.processEvent(Event.OperationSuccessed); + snapshot.processEvent(Event.OperationSucceeded); deleted = true; if (!resultIsSet) { result = true; @@ -196,7 +276,7 @@ private boolean deleteSnapshotChain(SnapshotInfo snapshot) { if (r) { List cacheSnaps = snapshotDataFactory.listSnapshotOnCache(snapshot.getId()); for (SnapshotInfo cacheSnap : cacheSnaps) { - logger.debug("Delete snapshot " + snapshot.getId() + " from image cache store: " + cacheSnap.getDataStore().getName()); + logger.debug("Delete snapshot {} from image cache store: {}", snapshot, cacheSnap.getDataStore()); cacheSnap.delete(); } } @@ -220,7 +300,7 @@ private boolean deleteSnapshotChain(SnapshotInfo snapshot) { } protected boolean areLastSnapshotRef(long snapshotId) { - List snapshotStoreRefs = _snapshotStoreDao.findBySnapshotId(snapshotId); + List snapshotStoreRefs = _snapshotStoreDao.findBySnapshotIdWithNonDestroyedState(snapshotId); if (CollectionUtils.isEmpty(snapshotStoreRefs) || snapshotStoreRefs.size() == 1) { return true; } @@ -230,48 +310,23 @@ protected boolean areLastSnapshotRef(long snapshotId) { protected boolean deleteSnapshotOnImageAndPrimary(long snapshotId, DataStore store) { SnapshotInfo snapshotOnImage = snapshotDataFactory.getSnapshot(snapshotId, store); SnapshotObject obj = (SnapshotObject)snapshotOnImage; - boolean areLastSnapshotRef = areLastSnapshotRef(snapshotId); - try { - if (areLastSnapshotRef) { - obj.processEvent(Snapshot.Event.DestroyRequested); - } - } catch (NoTransitionException e) { - logger.debug("Failed to set the state to destroying: ", e); - return false; - } + boolean result = false; try { - boolean result = deleteSnapshotChain(snapshotOnImage); + result = deleteSnapshotChain(snapshotOnImage); _snapshotStoreDao.updateDisplayForSnapshotStoreRole(snapshotId, store.getId(), store.getRole(), false); - if (areLastSnapshotRef) { - obj.processEvent(Snapshot.Event.OperationSucceeded); - } - if (result) { - SnapshotDataStoreVO snapshotOnPrimary = _snapshotStoreDao.findOneBySnapshotAndDatastoreRole(snapshotOnImage.getSnapshotId(), DataStoreRole.Primary); - if (snapshotOnPrimary != null) { - snapshotOnPrimary.setState(State.Destroyed); - _snapshotStoreDao.update(snapshotOnPrimary.getId(), snapshotOnPrimary); - } - } } catch (Exception e) { logger.debug("Failed to delete snapshot: ", e); - try { - if (areLastSnapshotRef) { - obj.processEvent(Snapshot.Event.OperationFailed); - } - } catch (NoTransitionException e1) { - logger.debug("Failed to change snapshot state: " + e.toString()); - } return false; } - return true; + return result; } private boolean deleteSnapshotFromDbIfNeeded(SnapshotVO snapshotVO, Long zoneId) { final long snapshotId = snapshotVO.getId(); SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(snapshotId, snapshotVO.getUuid()); if (snapshotDetails != null) { - _snapshotDetailsDao.removeDetails(snapshotId); + _snapshotDetailsDao.remove(snapshotId); } if (zoneId != null && List.of(Snapshot.State.Allocated, Snapshot.State.CreatedOnPrimary).contains(snapshotVO.getState())) { @@ -288,7 +343,7 @@ private boolean deleteSnapshotFromDbIfNeeded(SnapshotVO snapshotVO, Long zoneId) } if (Snapshot.State.Error.equals(snapshotVO.getState())) { - List storeRefs = _snapshotStoreDao.findBySnapshotId(snapshotId); + List storeRefs = _snapshotStoreDao.findBySnapshotIdWithNonDestroyedState(snapshotId); List deletedRefs = new ArrayList<>(); for (SnapshotDataStoreVO ref : storeRefs) { boolean refZoneIdMatch = false; @@ -307,19 +362,15 @@ private boolean deleteSnapshotFromDbIfNeeded(SnapshotVO snapshotVO, Long zoneId) return true; } - if (snapshotVO.getState() == Snapshot.State.CreatedOnPrimary) { - snapshotVO.setState(Snapshot.State.Destroyed); - _snapshotDao.update(snapshotId, snapshotVO); - return true; - } - if (!Snapshot.State.BackedUp.equals(snapshotVO.getState()) && !Snapshot.State.Error.equals(snapshotVO.getState()) && !Snapshot.State.Destroying.equals(snapshotVO.getState())) { - throw new InvalidParameterValueException("Can't delete snapshot " + snapshotId + " due to it is in " + snapshotVO.getState() + " Status"); + throw new InvalidParameterValueException(String.format("Can't delete snapshot %s due to it is in %s Status", snapshotVO, snapshotVO.getState())); } - List storeRefs = _snapshotStoreDao.listReadyBySnapshot(snapshotId, DataStoreRole.Image); + List storeRefs = _snapshotStoreDao.listBySnapshotAndDataStoreRole(snapshotId, DataStoreRole.Image); if (zoneId != null) { storeRefs.removeIf(ref -> !zoneId.equals(dataStoreMgr.getStoreZoneId(ref.getDataStoreId(), ref.getRole()))); + } else { + storeRefs.removeIf(ref -> !ref.getState().equals(State.Ready)); } for (SnapshotDataStoreVO ref : storeRefs) { if (!deleteSnapshotOnImageAndPrimary(snapshotId, dataStoreMgr.getDataStore(ref.getDataStoreId(), ref.getRole()))) { @@ -331,9 +382,20 @@ private boolean deleteSnapshotFromDbIfNeeded(SnapshotVO snapshotVO, Long zoneId) } else { snapshotZoneDao.removeSnapshotFromZones(snapshotVO.getId()); } + if (CollectionUtils.isNotEmpty(retrieveSnapshotEntries(snapshotId, null))) { + return true; + } return true; } + private List retrieveSnapshotEntries(long snapshotId, Long zoneId) { + return snapshotDataFactory.getSnapshots(snapshotId, zoneId); + } + + private void updateSnapshotToDestroyed(SnapshotVO snapshotVo) { + snapshotVo.setState(Snapshot.State.Destroyed); + _snapshotDao.update(snapshotVo.getId(), snapshotVo); + } @Override public SnapshotInfo takeSnapshot(SnapshotInfo snapshot) { @@ -348,4 +410,104 @@ public boolean revertSnapshot(SnapshotInfo snapshot) { @Override public void postSnapshotCreation(SnapshotInfo snapshot) { } + + @Override + public void copySnapshot(DataObject snapshot, DataObject snapshotDest, AsyncCompletionCallback callback) { + // export snapshot on remote + StoragePoolVO storagePoolVO = _primaryDataStoreDao.findById(snapshotDest.getDataStore().getId()); + String location = StorPoolConfigurationManager.StorPoolClusterLocation.valueIn(snapshotDest.getDataStore().getId()); + StorPoolUtil.spLog("StorpoolSnapshotStrategy.copySnapshot: snapshot %s to pool=%s", snapshot.getUuid(), storagePoolVO.getName()); + SnapshotInfo srcSnapshot = (SnapshotInfo) snapshot; + SnapshotInfo destSnapshot = (SnapshotInfo) snapshotDest; + String err = null; + String snapshotName = StorPoolStorageAdaptor.getVolumeNameFromPath(srcSnapshot.getPath(), false); + if (location != null) { + SpApiResponse resp = exportSnapshot(snapshot, location, snapshotName); + if (resp.getError() != null) { + err = String.format("Failed to export snapshot [{}] from [{}] due to [{}]", snapshotName, location, resp.getError()); + StorPoolUtil.spLog(err); + completeCallback(callback, destSnapshot.getPath(), err); + return; + } + keepExportedSnapshot(snapshot, location, snapshotName); + + SpConnectionDesc connectionRemote = StorPoolUtil.getSpConnection(storagePoolVO.getUuid(), + storagePoolVO.getId(), storagePoolDetailsDao, _primaryDataStoreDao); + SpApiResponse respFromRemote = copySnapshotFromRemote(snapshot, storagePoolVO, snapshotName, connectionRemote); + + if (respFromRemote.getError() != null) { + err = String.format("Failed to copy snapshot [{}] to [{}] due to [{}]", snapshotName, location, respFromRemote.getError()); + StorPoolUtil.spLog(err); + completeCallback(callback, destSnapshot.getPath(), err); + return; + } + StorPoolUtil.spLog("The snapshot [%s] was copied from remote", snapshotName); + + respFromRemote = StorPoolUtil.snapshotReconcile("~" + snapshotName, connectionRemote); + if (respFromRemote.getError() != null) { + err = String.format("Failed to reconcile snapshot [{}] from [{}] due to [{}]", snapshotName, location, respFromRemote.getError()); + StorPoolUtil.spLog(err); + completeCallback(callback, destSnapshot.getPath(), err); + return; + } + updateSnapshotPath(snapshotDest, srcSnapshot, destSnapshot); + } else { + completeCallback(callback, destSnapshot.getPath(), "The snapshot is not in the right location"); + } + SnapshotObjectTO snap = (SnapshotObjectTO) snapshotDest.getTO(); + snap.setPath(srcSnapshot.getPath()); + completeCallback(callback, destSnapshot.getPath(), err); + } + + private void completeCallback(AsyncCompletionCallback callback, String snapshotPath, String err) { + CreateCmdResult res = new CreateCmdResult(snapshotPath, null); + res.setResult(err); + callback.complete(res); + } + + private void updateSnapshotPath(DataObject snapshotDest, SnapshotInfo srcSnapshot, SnapshotInfo destSnapshot) { + + SnapshotDataStoreVO snapshotStore = _snapshotStoreDao.findByStoreSnapshot(DataStoreRole.Primary, snapshotDest.getDataStore().getId(), destSnapshot.getSnapshotId()); + snapshotStore.setInstallPath(srcSnapshot.getPath()); + _snapshotStoreDao.update(snapshotStore.getId(), snapshotStore); + } + + @NotNull + private SpApiResponse copySnapshotFromRemote(DataObject snapshot, StoragePoolVO storagePoolVO, String snapshotName, SpConnectionDesc connectionRemote) { + + String localLocation = StorPoolConfigurationManager.StorPoolClusterLocation + .valueIn(snapshot.getDataStore().getId()); + StoragePoolDetailVO template = storagePoolDetailsDao.findDetail(storagePoolVO.getId(), + StorPoolUtil.SP_TEMPLATE); + Map tags = addStorPoolTags(snapshot); + SpApiResponse respFromRemote = StorPoolUtil.snapshotFromRemote(snapshotName, localLocation, + template.getValue(), tags, connectionRemote); + return respFromRemote; + } + + @NotNull + private static Map addStorPoolTags(DataObject snapshot) { + Map tags = new HashMap<>(); + tags.put("cs", "snapshot"); + tags.put("uuid", snapshot.getUuid()); + return tags; + } + + private void keepExportedSnapshot(DataObject snapshot, String location, String snapshotName) { + + String detail = "~" + snapshotName + ";" + location; + SnapshotDetailsVO snapshotForRecovery = new SnapshotDetailsVO(snapshot.getId(), StorPoolUtil.SP_RECOVERED_SNAPSHOT, detail, true); + _snapshotDetailsDao.persist(snapshotForRecovery); + } + + @NotNull + private SpApiResponse exportSnapshot(DataObject snapshot, String location, String snapshotName) { + + SpConnectionDesc connectionLocal = StorPoolUtil.getSpConnection(snapshot.getDataStore().getUuid(), + snapshot.getDataStore().getId(), storagePoolDetailsDao, _primaryDataStoreDao); + Long clusterId = StorPoolHelper.findClusterIdByGlobalId(StorPoolUtil.getSnapshotClusterId("~" + snapshotName, connectionLocal), clusterDao); + connectionLocal = StorPoolHelper.getSpConnectionDesc(connectionLocal, clusterId); + SpApiResponse resp = StorPoolUtil.snapshotExport("~" + snapshotName, location, connectionLocal); + return resp; + } } diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolVMSnapshotStrategy.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolVMSnapshotStrategy.java index 2596b6a5bde7..f93260204a72 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolVMSnapshotStrategy.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolVMSnapshotStrategy.java @@ -148,7 +148,7 @@ public VMSnapshot takeVMSnapshot(VMSnapshot vmSnapshot) { vmSnapshot.getId(), StorPoolUtil.SP_STORAGE_POOL_ID, String.valueOf(poolId), false); vmSnapshotDetailsDao.persist(vmSnapshotDetailStoragePoolId); } - StorPoolUtil.spLog("Snapshot=%s of volume=%s for a group snapshot=%s.", snapshot, vol.getUuid(), vmSnapshot.getUuid()); + StorPoolUtil.spLog("Snapshot=%s of volume=%s for a group snapshot=%s.", snapshot, vol, vmSnapshot); } } } @@ -165,12 +165,12 @@ public VMSnapshot takeVMSnapshot(VMSnapshot vmSnapshot) { } publishUsageEvents(EventTypes.EVENT_VM_SNAPSHOT_ON_PRIMARY, vmSnapshot, userVm, new_chain_size - prev_chain_size, virtual_size); } else { - throw new CloudRuntimeException("Could not create vm snapshot"); + throw new CloudRuntimeException("Could not create Instance Snapshot"); } return vmSnapshot; } catch (Exception e) { - logger.debug("Could not create VM snapshot:" + e.getMessage()); - throw new CloudRuntimeException("Could not create VM snapshot:" + e.getMessage()); + logger.debug("Could not create Instance Snapshot:" + e.getMessage()); + throw new CloudRuntimeException("Could not create Instance Snapshot:" + e.getMessage()); } finally { if (!result) { try { @@ -217,9 +217,9 @@ public boolean deleteVMSnapshot(VMSnapshot vmSnapshot) { try { vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.ExpungeRequested); } catch (NoTransitionException e) { - logger.debug("Failed to change vm snapshot state with event ExpungeRequested"); + logger.debug("Failed to change Instance Snapshot state with event ExpungeRequested"); throw new CloudRuntimeException( - "Failed to change vm snapshot state with event ExpungeRequested: " + e.getMessage()); + "Failed to change Instance Snapshot state with event ExpungeRequested: " + e.getMessage()); } List volumeTOs = vmSnapshotHelper.getVolumeTOList(vmSnapshot.getVmId()); @@ -237,15 +237,15 @@ public boolean deleteVMSnapshot(VMSnapshot vmSnapshot) { VMSnapshotDetailsVO snapshotDetailsVO = vmSnapshotDetailsDao.findDetail(vmSnapshot.getId(), volumeObjectTO.getUuid()); String snapshotName = StorPoolStorageAdaptor.getVolumeNameFromPath(snapshotDetailsVO.getValue(), true); if (snapshotName == null) { - err = String.format("Could not find StorPool's snapshot vm snapshot uuid=%s and volume uui=%s", - vmSnapshot.getUuid(), volumeObjectTO.getUuid()); - logger.error("Could not delete snapshot for vm:" + err); + err = String.format("Could not find StorPool's Snapshot Instance Snapshot %s and volume [ID: %s, UUID: %s, name: %s]", + vmSnapshot, volumeObjectTO.getId(), volumeObjectTO.getUuid(), volumeObjectTO.getName()); + logger.error("Could not delete Snapshot for Instance:" + err); } StorPoolUtil.spLog("StorpoolVMSnapshotStrategy.deleteVMSnapshot snapshotName=%s", snapshotName); resp = StorPoolUtil.snapshotDelete(snapshotName, conn); if (resp.getError() != null) { - err = String.format("Could not delete storpool vm error=%s", resp.getError()); - logger.error("Could not delete snapshot for vm:" + err); + err = String.format("Could not delete storpool Instance error=%s", resp.getError()); + logger.error("Could not delete Snapshot for Instance:" + err); } else { // do we need to clean database? if (snapshotDetailsVO != null) { @@ -254,10 +254,9 @@ public boolean deleteVMSnapshot(VMSnapshot vmSnapshot) { } if (err != null) { StorPoolUtil.spLog( - "StorpoolVMSnapshotStrategy.deleteVMSnapshot delete snapshot=%s of gropusnapshot=%s failed due to %s", - snapshotName, userVm.getInstanceName(), err); - throw new CloudRuntimeException("Delete vm snapshot " + vmSnapshot.getName() + " of vm " - + userVm.getInstanceName() + " failed due to " + err); + "StorpoolVMSnapshotStrategy.deleteVMSnapshot delete snapshot=%s of group snapshot=%s failed due to %s", + snapshotName, userVm, err); + throw new CloudRuntimeException(String.format("Delete Instance Snapshot %s of Instance %s failed due to %s", vmSnapshot, userVm, err)); } } vmSnapshotDetailsDao.removeDetails(vmSnapshot.getId()); @@ -276,12 +275,12 @@ public boolean deleteVMSnapshot(VMSnapshot vmSnapshot) { @Override public boolean revertVMSnapshot(VMSnapshot vmSnapshot) { - logger.debug("Revert vm snapshot"); + logger.debug("Revert Instance Snapshot"); VMSnapshotVO vmSnapshotVO = (VMSnapshotVO) vmSnapshot; UserVmVO userVm = userVmDao.findById(vmSnapshot.getVmId()); if (userVm.getState() == VirtualMachine.State.Running && vmSnapshotVO.getType() == VMSnapshot.Type.Disk) { - throw new CloudRuntimeException("Virtual machine should be in stopped state for revert operation"); + throw new CloudRuntimeException("The Instance should be in stopped state for revert operation"); } try { @@ -302,9 +301,9 @@ public boolean revertVMSnapshot(VMSnapshot vmSnapshot) { volumeObjectTO.getUuid()); String snapshotName = StorPoolStorageAdaptor.getVolumeNameFromPath(snapshotDetailsVO.getValue(), true); if (snapshotName == null) { - err = String.format("Could not find StorPool's snapshot vm snapshot uuid=%s and volume uui=%s", + err = String.format("Could not find StorPool's Snapshot Instance Snapshot uuid=%s and volume uui=%s", vmSnapshot.getUuid(), volumeObjectTO.getUuid()); - logger.error("Could not delete snapshot for vm:" + err); + logger.error("Could not delete Snapshot for Instance: " + err); } String volumeName = StorPoolStorageAdaptor.getVolumeNameFromPath(volumeObjectTO.getPath(), true); VolumeDetailVO detail = volumeDetailsDao.findDetail(volumeObjectTO.getId(), StorPoolUtil.SP_PROVIDER_NAME); @@ -344,7 +343,7 @@ public boolean revertVMSnapshot(VMSnapshot vmSnapshot) { finalizeRevert(vmSnapshotVO, volumeTOs); result = vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationSucceeded); } catch (CloudRuntimeException | NoTransitionException e) { - String errMsg = String.format("Error while finalize create vm snapshot [%s] due to %s", vmSnapshot.getName(), e.getMessage()); + String errMsg = String.format("Error while finalize create Instance Snapshot [%s] due to %s", vmSnapshot, e.getMessage()); logger.error(errMsg, e); throw new CloudRuntimeException(errMsg); } finally { @@ -352,7 +351,7 @@ public boolean revertVMSnapshot(VMSnapshot vmSnapshot) { try { vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationFailed); } catch (NoTransitionException e1) { - logger.error("Cannot set vm snapshot state due to: " + e1.getMessage()); + logger.error("Cannot set Instance Snapshot state due to: " + e1.getMessage()); } } } diff --git a/plugins/storage/volume/storpool/src/main/resources/META-INF/cloudstack/storage-volume-storpool/spring-storage-volume-storpool-context.xml b/plugins/storage/volume/storpool/src/main/resources/META-INF/cloudstack/storage-volume-storpool/spring-storage-volume-storpool-context.xml index 6451fc8fd398..b4e81f166f00 100644 --- a/plugins/storage/volume/storpool/src/main/resources/META-INF/cloudstack/storage-volume-storpool/spring-storage-volume-storpool-context.xml +++ b/plugins/storage/volume/storpool/src/main/resources/META-INF/cloudstack/storage-volume-storpool/spring-storage-volume-storpool-context.xml @@ -1,12 +1,12 @@ - org.apache.cloudstack cloudstack-plugins - 4.20.0.0-SNAPSHOT + 4.23.0.0-SNAPSHOT ../../pom.xml - 2.0.0.AM25 - 1.5 + 2.0.0.AM27 1.3.2 1.1.3 1.1-groovy-2.4 @@ -41,18 +40,21 @@ - org.codehaus.gmaven - gmaven-plugin - ${gmaven.version} - - 1.7 - - + org.codehaus.gmavenplus + gmavenplus-plugin + ${cs.gmavenplus.version} + + + org.codehaus.groovy + groovy-all + ${cs.groovy.version} + + compile - testCompile + compileTests @@ -66,29 +68,12 @@ - - - org.codehaus.gmaven.runtime - gmaven-runtime-1.7 - ${gmaven.version} - - - org.codehaus.groovy - groovy-all - - - - - org.codehaus.groovy - groovy-all - ${cs.groovy.version} - - org.apache.maven.plugins maven-surefire-plugin + @{argLine} --add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED **/*Spec.groovy **/*Test.java @@ -149,7 +134,7 @@
org.mockito - mockito-inline + mockito-core ${cs.mockito.version} compile @@ -162,7 +147,7 @@ net.bytebuddy byte-buddy - 1.14.5 + 1.15.11 junit @@ -230,5 +215,11 @@ commons-io ${cs.commons-io.version} + + org.apache.cloudstack + cloud-api + ${project.version} + compile +
diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapAddConfigurationCmd.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapAddConfigurationCmd.java index 1131667d98ae..0f6e43c38fed 100644 --- a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapAddConfigurationCmd.java +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapAddConfigurationCmd.java @@ -46,7 +46,7 @@ public class LdapAddConfigurationCmd extends BaseCmd { @Parameter(name = ApiConstants.PORT, type = CommandType.INTEGER, required = true, description = "Port") private int port; - @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, required = false, entityType = DomainResponse.class, description = "linked domain") + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "Linked Domain") private Long domainId; public LdapAddConfigurationCmd() { diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapCreateAccountCmd.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapCreateAccountCmd.java index 880ecea4d13c..3aa3e0502268 100644 --- a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapCreateAccountCmd.java +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapCreateAccountCmd.java @@ -21,6 +21,7 @@ import com.cloud.user.AccountService; import com.cloud.user.User; import com.cloud.user.UserAccount; +import com.cloud.utils.StringUtils; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -39,7 +40,6 @@ import org.bouncycastle.util.encoders.Base64; import javax.inject.Inject; -import java.io.UnsupportedEncodingException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Map; @@ -72,7 +72,7 @@ public class LdapCreateAccountCmd extends BaseCmd { @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") @@ -107,7 +107,7 @@ public Account.Type getAccountType() { if (accountType == null) { return RoleType.getAccountTypeByRole(roleService.findRole(roleId), null); } - return RoleType.getAccountTypeByRole(roleService.findRole(roleId), Account.Type.getFromValue(accountType.intValue())); + return RoleType.getAccountTypeByRole(roleService.findRole(roleId), Account.Type.getFromValue(accountType)); } public Long getRoleId() { @@ -138,7 +138,7 @@ public void execute() throws ServerApiException { final CallContext callContext = getCurrentContext(); String finalAccountName = getAccountName(); Long finalDomainId = getDomainId(); - callContext.setEventDetails("Account Name: " + finalAccountName + ", Domain Id:" + finalDomainId); + callContext.setEventDetails("Account Name: " + finalAccountName + ", Domain ID:" + finalDomainId); try { final LdapUser user = _ldapManager.getUser(username, domainId); validateUser(user); @@ -158,10 +158,10 @@ public void execute() throws ServerApiException { private String generatePassword() throws ServerApiException { try { final SecureRandom randomGen = SecureRandom.getInstance("SHA1PRNG"); - final byte bytes[] = new byte[20]; + final byte[] bytes = new byte[20]; randomGen.nextBytes(bytes); - return new String(Base64.encode(bytes), "UTF-8"); - } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { + return new String(Base64.encode(bytes), StringUtils.getPreferredCharset()); + } catch (NoSuchAlgorithmException e) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to generate random password"); } } @@ -180,7 +180,7 @@ public long getEntityOwnerId() { return Account.ACCOUNT_ID_SYSTEM; } - private boolean validateUser(final LdapUser user) throws ServerApiException { + private void validateUser(final LdapUser user) throws ServerApiException { if (user.getEmail() == null) { throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, username + " has no email address set within LDAP"); } @@ -190,6 +190,5 @@ private boolean validateUser(final LdapUser user) throws ServerApiException { if (user.getLastname() == null) { throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, username + " has no lastname set within LDAP"); } - return true; } } diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapDeleteConfigurationCmd.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapDeleteConfigurationCmd.java index 15e6c836d0db..d2d4e5a5342d 100644 --- a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapDeleteConfigurationCmd.java +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapDeleteConfigurationCmd.java @@ -40,14 +40,16 @@ public class LdapDeleteConfigurationCmd extends BaseCmd { @Inject private LdapManager _ldapManager; + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, required = false, entityType = LdapConfigurationResponse.class, description = "ID of the LDAP configuration") + private Long id; - @Parameter(name = ApiConstants.HOST_NAME, type = CommandType.STRING, required = true, description = "Hostname") + @Parameter(name = ApiConstants.HOST_NAME, type = CommandType.STRING, description = "Hostname") private String hostname; - @Parameter(name = ApiConstants.PORT, type = CommandType.INTEGER, required = false, description = "port") + @Parameter(name = ApiConstants.PORT, type = CommandType.INTEGER, description = "Port") private int port; - @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, required = false, entityType = DomainResponse.class, description = "linked domain") + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "Linked Domain") private Long domainId; public LdapDeleteConfigurationCmd() { @@ -71,6 +73,10 @@ public Long getDomainId() { return domainId; } + public Long getId() { + return id; + } + @Override public void execute() throws ServerApiException { try { diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapImportUsersCmd.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapImportUsersCmd.java index 087bd63c2969..d6df27ccfcb9 100644 --- a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapImportUsersCmd.java +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapImportUsersCmd.java @@ -16,7 +16,6 @@ // under the License. package org.apache.cloudstack.api.command; -import java.io.UnsupportedEncodingException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.ArrayList; @@ -41,7 +40,6 @@ import org.apache.cloudstack.ldap.LdapManager; import org.apache.cloudstack.ldap.LdapUser; import org.apache.cloudstack.ldap.NoLdapUserMatchingQueryException; -import org.apache.commons.lang3.StringUtils; import org.bouncycastle.util.encoders.Base64; import com.cloud.domain.Domain; @@ -56,6 +54,7 @@ import com.cloud.user.DomainService; import com.cloud.user.User; import com.cloud.user.UserAccount; +import com.cloud.utils.StringUtils; @APICommand(name = "importLdapUsers", description = "Import LDAP users", responseObject = LdapUserResponse.class, since = "4.3.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class LdapImportUsersCmd extends BaseListCmd { @@ -72,7 +71,7 @@ public class LdapImportUsersCmd extends BaseListCmd { @Parameter(name = ApiConstants.ROLE_ID, type = CommandType.UUID, entityType = RoleResponse.class, description = "Creates the account under the specified role.") private Long roleId; - @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.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "Specifies the domain to which the ldap users are to be " @@ -106,18 +105,18 @@ public LdapImportUsersCmd(final LdapManager ldapManager, final DomainService dom private void createCloudstackUserAccount(LdapUser user, String accountName, Domain domain) { Account account = _accountService.getActiveAccountByName(accountName, domain.getId()); if (account == null) { - logger.debug("No account exists with name: " + accountName + " creating the account and an user with name: " + user.getUsername() + " in the account"); + logger.debug("No account exists with name: {}. Creating the account and a user with name: {} in the account", accountName, user.getUsername()); _accountService.createUserAccount(user.getUsername(), generatePassword(), user.getFirstname(), user.getLastname(), user.getEmail(), timezone, accountName, getAccountType(), getRoleId(), domain.getId(), domain.getNetworkDomain(), details, UUID.randomUUID().toString(), UUID.randomUUID().toString(), User.Source.LDAP); } else { // check if the user exists. if yes, call update UserAccount csuser = _accountService.getActiveUserAccount(user.getUsername(), domain.getId()); if (csuser == null) { - logger.debug("No user exists with name: " + user.getUsername() + " creating a user in the account: " + accountName); + logger.debug("No user exists with name: {}. Creating a user in the account: {}", user.getUsername(), accountName); _accountService.createUser(user.getUsername(), generatePassword(), user.getFirstname(), user.getLastname(), user.getEmail(), timezone, accountName, domain.getId(), UUID.randomUUID().toString(), User.Source.LDAP); } else { - logger.debug("Account [name=%s] and user [name=%s] already exist in CloudStack. Executing the user update."); + logger.debug("Account [name={}] and user [name={}] already exist in CloudStack. Executing the user update.", account, csuser); UpdateUserCmd updateUserCmd = new UpdateUserCmd(); updateUserCmd.setId(csuser.getId()); @@ -145,21 +144,21 @@ public void execute() users = _ldapManager.getUsers(domainId); } } catch (NoLdapUserMatchingQueryException ex) { - users = new ArrayList(); - logger.info("No Ldap user matching query. " + " ::: " + ex.getMessage()); + users = new ArrayList<>(); + logger.info("No Ldap user matching query. ::: {}", ex.getMessage()); } - List addedUsers = new ArrayList(); + List addedUsers = new ArrayList<>(); for (LdapUser user : users) { Domain domain = getDomain(user); try { createCloudstackUserAccount(user, getAccountName(user), domain); addedUsers.add(user); } catch (InvalidParameterValueException ex) { - logger.error("Failed to create user with username: " + user.getUsername() + " ::: " + ex.getMessage()); + logger.error("Failed to create user with username: {} ::: {}", user.getUsername(), ex.getMessage()); } } - ListResponse response = new ListResponse(); + ListResponse response = new ListResponse<>(); response.setResponses(createLdapUserResponse(addedUsers)); response.setResponseName(getCommandName()); setResponseObject(response); @@ -169,7 +168,7 @@ public Account.Type getAccountType() { if (accountType == null) { return RoleType.getAccountTypeByRole(roleService.findRole(roleId), null); } - return RoleType.getAccountTypeByRole(roleService.findRole(roleId), Account.Type.getFromValue(accountType.intValue())); + return RoleType.getAccountTypeByRole(roleService.findRole(roleId), Account.Type.getFromValue(accountType)); } public Long getRoleId() { @@ -202,11 +201,11 @@ private Domain getDomainForName(String name) { private Domain getDomain(LdapUser user) { Domain domain; if (_domain != null) { - //this means either domain id or groupname is passed and this will be same for all the users in this call. hence returning it. + // This means that either a domain id or group name is passed and this will be same for all the users in this call, hence returning it. domain = _domain; } else { if (domainId != null) { - // a domain Id is passed. use it for this user and all the users in the same api call (by setting _domain) + // a domain ID is passed. Use it for this user and all the users in the same API call (by setting _domain) domain = _domain = _domainService.getDomain(domainId); } else { // a group name is passed. use it for this user and all the users in the same api call(by setting _domain) @@ -225,7 +224,7 @@ private Domain getDomain(LdapUser user) { } private List createLdapUserResponse(List users) { - final List ldapResponses = new ArrayList(); + final List ldapResponses = new ArrayList<>(); for (final LdapUser user : users) { final LdapUserResponse ldapResponse = _ldapManager.createLdapUserResponse(user); ldapResponse.setObjectName("LdapUser"); @@ -242,10 +241,10 @@ public String getCommandName() { private String generatePassword() throws ServerApiException { try { final SecureRandom randomGen = SecureRandom.getInstance("SHA1PRNG"); - final byte bytes[] = new byte[20]; + final byte[] bytes = new byte[20]; randomGen.nextBytes(bytes); - return new String(Base64.encode(bytes), "UTF-8"); - } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { + return new String(Base64.encode(bytes), StringUtils.getPreferredCharset()); + } catch (NoSuchAlgorithmException e) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to generate random password"); } } diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapListConfigurationCmd.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapListConfigurationCmd.java index c34d026f89b2..2904a72f20ac 100644 --- a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapListConfigurationCmd.java +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapListConfigurationCmd.java @@ -44,17 +44,22 @@ public class LdapListConfigurationCmd extends BaseListCmd { @Inject private LdapManager _ldapManager; - @Parameter(name = ApiConstants. HOST_NAME, type = CommandType.STRING, required = false, description = "Hostname") + @Parameter(name = ApiConstants. HOST_NAME, type = CommandType.STRING, description = "Hostname") private String hostname; - @Parameter(name = ApiConstants.PORT, type = CommandType.INTEGER, required = false, description = "Port") + @Parameter(name = ApiConstants.PORT, type = CommandType.INTEGER, description = "Port") private int port; - @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, required = false, entityType = DomainResponse.class, description = "linked domain") + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, + description = "Linked Domain") private Long domainId; - @Parameter(name = ApiConstants.LIST_ALL, type = CommandType.BOOLEAN, description = "If set to true, " - + " and no domainid specified, list all LDAP configurations irrespective of the linked domain", since = "4.13.2") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = LdapConfigurationResponse.class, description = "list ldap configuration by ID; when passed, all other parameters are ignored") + private Long id; + + @Parameter(name = ApiConstants.LIST_ALL, type = CommandType.BOOLEAN, + description = "If set to true, and no `domainid` specified, list all LDAP configurations irrespective of the linked domain", + since = "4.13.2") private Boolean listAll; public LdapListConfigurationCmd() { @@ -67,7 +72,7 @@ public LdapListConfigurationCmd(final LdapManager ldapManager) { } private List createLdapConfigurationResponses(final List configurations) { - final List responses = new ArrayList(); + final List responses = new ArrayList<>(); for (final LdapConfigurationVO resource : configurations) { final LdapConfigurationResponse configurationResponse = _ldapManager.createLdapConfigurationResponse(resource); configurationResponse.setObjectName("LdapConfiguration"); @@ -80,7 +85,7 @@ private List createLdapConfigurationResponses(final L public void execute() { final Pair, Integer> result = _ldapManager.listConfigurations(this); final List responses = createLdapConfigurationResponses(result.first()); - final ListResponse response = new ListResponse(); + final ListResponse response = new ListResponse<>(); response.setResponses(responses, result.second()); response.setResponseName(getCommandName()); setResponseObject(response); @@ -120,6 +125,10 @@ public void setDomainId(final Long domainId) { this.domainId = domainId; } + public Long getId() { + return id; + } + public boolean listAll() { return listAll != null && listAll; } diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapListUsersCmd.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapListUsersCmd.java index e5d434d38108..c5f6b6f074c1 100644 --- a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapListUsersCmd.java +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapListUsersCmd.java @@ -16,10 +16,9 @@ // under the License. package org.apache.cloudstack.api.command; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import javax.inject.Inject; @@ -44,9 +43,10 @@ import org.apache.cloudstack.query.QueryService; import com.cloud.user.Account; +import org.apache.commons.collections.CollectionUtils; /** - * a short flow, use plantuml to view (see http://plantuml.com) + * a short flow, use plantuml to view (see the plantuml site) * @startuml * start * :list ldap users request; @@ -84,14 +84,12 @@ public class LdapListUsersCmd extends BaseListCmd { @Parameter(name = "listtype", type = CommandType.STRING, - required = false, description = "Determines whether all ldap users are returned or just non-cloudstack users. This option is deprecated in favour for the more option rich 'userfilter' parameter") @Deprecated private String listType; @Parameter(name = ApiConstants.USER_FILTER, type = CommandType.STRING, - required = false, since = "4.13", description = "Determines what type of filter is applied on the list of users returned from LDAP.\n" + "\tvalid values are\n" @@ -102,7 +100,7 @@ public class LdapListUsersCmd extends BaseListCmd { + " including those that are already in cloudstack, the later will be annotated with their userSource") private String userFilter; - @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, required = false, entityType = DomainResponse.class, description = "linked domain") + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "Linked Domain") private Long domainId; public LdapListUsersCmd() { @@ -121,7 +119,7 @@ public LdapListUsersCmd(final LdapManager ldapManager, final QueryService queryS * @return a (filtered?) list of user response objects */ private List createLdapUserResponse(final List users) { - final List ldapResponses = new ArrayList(); + final List ldapResponses = new ArrayList<>(); for (final LdapUser user : users) { final LdapUserResponse ldapResponse = _ldapManager.createLdapUserResponse(user); ldapResponse.setObjectName("LdapUser"); @@ -135,15 +133,16 @@ private List createLdapUserResponse(final List users @Override public void execute() throws ServerApiException { cloudstackUsers = null; - List ldapResponses = new ArrayList(); - final ListResponse response = new ListResponse(); + List ldapResponses = new ArrayList<>(); + final ListResponse response = new ListResponse<>(); try { final List users = _ldapManager.getUsers(domainId); ldapResponses = createLdapUserResponse(users); -// now filter and annotate + // now filter and annotate ldapResponses = applyUserFilter(ldapResponses); } catch (final NoLdapUserMatchingQueryException ex) { - // ok, we'll make do with the empty list ldapResponses = new ArrayList(); + logger.debug(ex.getMessage()); + // ok, we'll make do with the empty list } finally { response.setResponses(ldapResponses); response.setResponseName(getCommandName()); @@ -176,16 +175,13 @@ private void traceUserList() { users.append(user.getUsername()); } - logger.trace(String.format("checking against %d cloudstackusers: %s.", this.cloudstackUsers.size(), users.toString())); + logger.trace("checking against {} cloudstackusers: {}.", this.cloudstackUsers.size(), users); } } private List applyUserFilter(List ldapResponses) { - if(logger.isTraceEnabled()) { - logger.trace(String.format("applying filter: %s or %s.", this.getListTypeString(), this.getUserFilter())); - } - List responseList = getUserFilter().filter(this,ldapResponses); - return responseList; + logger.trace("applying filter: {} or {}.", this.getListTypeString(), this.getUserFilter()); + return getUserFilter().filter(this,ldapResponses); } @Override @@ -210,48 +206,34 @@ UserFilter getUserFilter() { return UserFilter.fromString(getUserFilterString()); } - boolean isACloudstackUser(final LdapUser ldapUser) { + boolean isACloudStackUser(final LdapUser ldapUser) { + String username = ldapUser.getUsername(); + return isACloudStackUser(username); + } + + boolean isACloudStackUser(final LdapUserResponse ldapUser) { + logger.trace("checking response : {}", ldapUser.toString()); + String username = ldapUser.getUsername(); + return isACloudStackUser(username); + } + + private boolean isACloudStackUser(String username) { boolean rc = false; final List cloudstackUsers = getCloudstackUsers(); - if (cloudstackUsers != null) { + if (CollectionUtils.isNotEmpty(cloudstackUsers)) { for (final UserResponse cloudstackUser : cloudstackUsers) { - if (ldapUser.getUsername().equals(cloudstackUser.getUsername())) { - if(logger.isTraceEnabled()) { - logger.trace(String.format("found user %s in cloudstack", ldapUser.getUsername())); - } - + if (username.equals(cloudstackUser.getUsername())) { + logger.trace("Found user {} in CloudStack", cloudstackUser.getUsername()); rc = true; + break; } else { - if(logger.isTraceEnabled()) { - logger.trace(String.format("ldap user %s does not match cloudstack user %s", ldapUser.getUsername(), cloudstackUser.getUsername())); - } + logger.trace("ldap user {} does not match cloudstack user {}", username, cloudstackUser.getUsername()); } } } return rc; } - boolean isACloudstackUser(final LdapUserResponse ldapUser) { - if(logger.isTraceEnabled()) { - logger.trace("checking response : " + ldapUser.toString()); - } - final List cloudstackUsers = getCloudstackUsers(); - if (cloudstackUsers != null && cloudstackUsers.size() != 0) { - for (final UserResponse cloudstackUser : cloudstackUsers) { - if (ldapUser.getUsername().equals(cloudstackUser.getUsername())) { - if(logger.isTraceEnabled()) { - logger.trace(String.format("found user %s in cloudstack user %s", ldapUser.getUsername(), cloudstackUser.getUsername())); - } - return true; - } else { - if(logger.isTraceEnabled()) { - logger.trace(String.format("ldap user %s does not match cloudstack user %s", ldapUser.getUsername(), cloudstackUser.getUsername())); - } - } - } - } - return false; - } /** * typecheck for userfilter values and filter type dependend functionalities. * This could have been in two switch statements elsewhere in the code. @@ -362,7 +344,7 @@ public List filterAnyDomain(List input) { if(logger.isTraceEnabled()) { logger.trace("filtering existing users"); } - final List ldapResponses = new ArrayList(); + final List ldapResponses = new ArrayList<>(); for (final LdapUserResponse user : input) { if (isNotAlreadyImportedInTheCurrentDomain(user)) { ldapResponses.add(user); @@ -395,7 +377,7 @@ public List filterLocalDomain(List input) { if(logger.isTraceEnabled()) { logger.trace("filtering local domain users"); } - final List ldapResponses = new ArrayList(); + final List ldapResponses = new ArrayList<>(); String domainId = getCurrentDomainId(); for (final LdapUserResponse user : input) { UserResponse cloudstackUser = getCloudstackUser(user); @@ -411,7 +393,7 @@ public List filterLocalDomain(List input) { } private String getCurrentDomainId() { - String domainId = null; + String domainId; if (this.domainId != null) { Domain domain = _domainService.getDomain(this.domainId); domainId = domain.getUuid(); @@ -432,10 +414,10 @@ public List filterPotentialImport(List input logger.trace("should be filtering potential imports!!!"); } // functional possibility do not add only users not yet in cloudstack but include users that would be moved if they are so in ldap? - // this means if they are part of a account linked to an ldap group/ou + // This means if they are part of an Account linked to an LDAP Group/OU input.removeIf(ldapUser -> ( - (isACloudstackUser(ldapUser)) + (isACloudStackUser(ldapUser)) && (getCloudstackUser(ldapUser).getUserSource().equalsIgnoreCase(User.Source.LDAP.toString())) ) ); @@ -465,7 +447,7 @@ private UserResponse getCloudstackUser(LdapUserResponse user) { for (final UserResponse cloudstackUser : cloudstackUsers) { if (user.getUsername().equals(cloudstackUser.getUsername())) { returnObject = cloudstackUser; - if (returnObject.getDomainId() == this.getCurrentDomainId()) { + if (Objects.equals(returnObject.getDomainId(), this.getCurrentDomainId())) { break; } } @@ -473,31 +455,4 @@ private UserResponse getCloudstackUser(LdapUserResponse user) { } return returnObject; } - - private void checkFilterMethodType(Type returnType) { - String msg = null; - if (returnType instanceof ParameterizedType) { - ParameterizedType type = (ParameterizedType) returnType; - if(type.getRawType().equals(List.class)) { - Type[] typeArguments = type.getActualTypeArguments(); - if (typeArguments.length == 1) { - if (typeArguments[0].equals(LdapUserResponse.class)) { - // we're good' - } else { - msg = new String("list of return type contains " + typeArguments[0].getTypeName()); - } - } else { - msg = String.format("type %s has to the wrong number of arguments", type.getRawType()); - } - } else { - msg = String.format("type %s is not a List<>", type.getTypeName()); - } - } else { - msg = new String("can't even begin to explain; review your method signature"); - } - if(msg != null) { - throw new IllegalArgumentException(msg); - } - } - } diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapUserSearchCmd.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapUserSearchCmd.java index b702beda1708..42fbca162047 100644 --- a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapUserSearchCmd.java +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LdapUserSearchCmd.java @@ -41,7 +41,7 @@ public class LdapUserSearchCmd extends BaseListCmd { @Inject private LdapManager _ldapManager; - @Parameter(name = "query", type = CommandType.STRING, entityType = LdapUserResponse.class, required = true, description = "query to search using") + @Parameter(name = "query", type = CommandType.STRING, entityType = LdapUserResponse.class, required = true, description = "Query to search using") private String query; public LdapUserSearchCmd() { @@ -54,7 +54,7 @@ public LdapUserSearchCmd(final LdapManager ldapManager) { } private List createLdapUserResponse(final List users) { - final List ldapUserResponses = new ArrayList(); + final List ldapUserResponses = new ArrayList<>(); if (users != null) { for (final LdapUser user : users) { final LdapUserResponse ldapUserResponse = _ldapManager.createLdapUserResponse(user); @@ -67,7 +67,7 @@ private List createLdapUserResponse(final List users @Override public void execute() { - final ListResponse response = new ListResponse(); + final ListResponse response = new ListResponse<>(); List users = null; try { diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LinkAccountToLdapCmd.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LinkAccountToLdapCmd.java index 7e2114ea00f7..d3ab463cffb2 100644 --- a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LinkAccountToLdapCmd.java +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LinkAccountToLdapCmd.java @@ -33,6 +33,7 @@ import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.LinkAccountToLdapResponse; import org.apache.cloudstack.api.response.LinkDomainToLdapResponse; +import org.apache.cloudstack.api.response.RoleResponse; import org.apache.cloudstack.ldap.LdapManager; import org.apache.cloudstack.ldap.LdapUser; import org.apache.cloudstack.ldap.NoLdapUserMatchingQueryException; @@ -42,28 +43,31 @@ import com.cloud.user.User; import com.cloud.user.UserAccount; -@APICommand(name = "linkAccountToLdap", description = "link a cloudstack account to a group or OU in ldap", responseObject = LinkDomainToLdapResponse.class, since = "4.11.0", +@APICommand(name = "linkAccountToLdap", description = "Link a cloudstack account to a group or OU in ldap", responseObject = LinkDomainToLdapResponse.class, since = "4.11.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin,RoleType.DomainAdmin}) public class LinkAccountToLdapCmd extends BaseCmd { @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, required = true, entityType = DomainResponse.class, description = "The id of the domain that is to contain the linked account.") private Long domainId; - @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, required = false, description = "type of the ldap name. GROUP or OU, defaults to GROUP") + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "Type of the LDAP name. GROUP or OU, defaults to GROUP") private String type; - @Parameter(name = ApiConstants.LDAP_DOMAIN, type = CommandType.STRING, required = true, description = "name of the group or OU in LDAP") + @Parameter(name = ApiConstants.LDAP_DOMAIN, type = CommandType.STRING, required = true, description = "Name of the group or OU in LDAP") private String ldapDomain; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, required = true, description = "name of the account, it will be created if it does not exist") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, required = true, description = "Name of the account, it will be created if it does not exist") private String accountName; - @Parameter(name = ApiConstants.ADMIN, type = CommandType.STRING, required = false, description = "domain admin username in LDAP ") + @Parameter(name = ApiConstants.ADMIN, type = CommandType.STRING, description = "Domain admin username in LDAP ") private String admin; - @Parameter(name = ApiConstants.ACCOUNT_TYPE, type = CommandType.INTEGER, required = true, description = "Type of the account to auto import. Specify 0 for user and 2 for " + @Parameter(name = ApiConstants.ACCOUNT_TYPE, type = CommandType.INTEGER, description = "Type of the account to auto import. Specify 0 for user and 2 for " + "domain admin") - private int accountType; + private Integer accountType; + + @Parameter(name = ApiConstants.ROLE_ID, type = CommandType.UUID, entityType = RoleResponse.class, description = "Creates the account under the specified role.", since="4.19.1") + private Long roleId; @Inject private LdapManager _ldapManager; @@ -77,7 +81,7 @@ public void execute() throws ServerApiException { try { ldapUser = _ldapManager.getUser(admin, type, ldapDomain, domainId); } catch (NoLdapUserMatchingQueryException e) { - logger.debug("no ldap user matching username " + admin + " in the given group/ou", e); + logger.debug("no ldap user matching username {} in the given group/ou", admin, e); } if (ldapUser != null && !ldapUser.isDisabled()) { Account account = _accountService.getActiveAccountByName(admin, domainId); @@ -87,15 +91,15 @@ public void execute() throws ServerApiException { .createUserAccount(admin, "", ldapUser.getFirstname(), ldapUser.getLastname(), ldapUser.getEmail(), null, admin, Account.Type.DOMAIN_ADMIN, RoleType.DomainAdmin.getId(), domainId, null, null, UUID.randomUUID().toString(), UUID.randomUUID().toString(), User.Source.LDAP); response.setAdminId(String.valueOf(userAccount.getAccountId())); - logger.info("created an account with name " + admin + " in the given domain " + domainId); + logger.info("created an account with name {} in the given domain {} with id {}", admin, _domainService.getDomain(domainId), domainId); } catch (Exception e) { - logger.info("an exception occurred while creating account with name " + admin + " in domain " + domainId, e); + logger.info("an exception occurred while creating account with name {} in domain {} with id {}", admin, _domainService.getDomain(domainId), domainId, e); } } else { - logger.debug("an account with name " + admin + " already exists in the domain " + domainId); + logger.debug("an account with name {} already exists in the domain {} with id {}", admin, _domainService.getDomain(domainId), domainId); } } else { - logger.debug("ldap user with username " + admin + " is disabled in the given group/ou"); + logger.debug("ldap user with username {} is disabled in the given group/ou", admin); } } response.setObjectName(this.getActualCommandName()); @@ -132,7 +136,14 @@ public String getAdmin() { } public Account.Type getAccountType() { - return Account.Type.getFromValue(accountType); + if (accountType == null) { + return RoleType.getAccountTypeByRole(roleService.findRole(roleId), null); + } + return RoleType.getAccountTypeByRole(roleService.findRole(roleId), Account.Type.getFromValue(accountType)); + } + + public Long getRoleId() { + return RoleType.getRoleByAccountType(roleId, getAccountType()); } @Override diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LinkDomainToLdapCmd.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LinkDomainToLdapCmd.java index d5187f99c995..7b1613eb7672 100644 --- a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LinkDomainToLdapCmd.java +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/LinkDomainToLdapCmd.java @@ -40,7 +40,7 @@ import java.util.UUID; -@APICommand(name = "linkDomainToLdap", description = "link an existing cloudstack domain to group or OU in ldap", responseObject = LinkDomainToLdapResponse.class, since = "4.6.0", +@APICommand(name = "linkDomainToLdap", description = "Link an existing Cloudstack domain to group or OU in ldap", responseObject = LinkDomainToLdapResponse.class, since = "4.6.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class LinkDomainToLdapCmd extends BaseCmd { @@ -48,17 +48,13 @@ public class LinkDomainToLdapCmd extends BaseCmd { + "linked to LDAP.") private Long domainId; - @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, required = true, description = "type of the ldap name. GROUP or OU") + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, required = true, description = "Type of the ldap name. GROUP or OU") private String type; - @Parameter(name = ApiConstants.LDAP_DOMAIN, type = CommandType.STRING, required = false, description = "name of the group or OU in LDAP") + @Parameter(name = ApiConstants.LDAP_DOMAIN, type = CommandType.STRING, required = true, description = "Name of the GROUP or OU in LDAP") private String ldapDomain; - @Deprecated - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = false, description = "name of the group or OU in LDAP") - private String name; - - @Parameter(name = ApiConstants.ADMIN, type = CommandType.STRING, required = false, description = "domain admin username in LDAP ") + @Parameter(name = ApiConstants.ADMIN, type = CommandType.STRING, description = "Domain admin username in LDAP ") private String admin; @Parameter(name = ApiConstants.ACCOUNT_TYPE, type = CommandType.INTEGER, required = true, description = "Type of the account to auto import. Specify 0 for user and 2 for " + @@ -77,7 +73,7 @@ public String getType() { } public String getLdapDomain() { - return ldapDomain == null ? name : ldapDomain; + return ldapDomain; } public String getAdmin() { @@ -98,7 +94,7 @@ public void execute() throws ServerApiException { try { ldapUser = _ldapManager.getUser(admin, type, getLdapDomain(), domainId); } catch (NoLdapUserMatchingQueryException e) { - logger.debug("no ldap user matching username " + admin + " in the given group/ou", e); + logger.debug("no ldap user matching username {} in the given group/ou", admin, e); } if (ldapUser != null && !ldapUser.isDisabled()) { Account account = _accountService.getActiveAccountByName(admin, domainId); @@ -107,15 +103,15 @@ public void execute() throws ServerApiException { UserAccount userAccount = _accountService.createUserAccount(admin, "", ldapUser.getFirstname(), ldapUser.getLastname(), ldapUser.getEmail(), null, admin, Account.Type.DOMAIN_ADMIN, RoleType.DomainAdmin.getId(), domainId, null, null, UUID.randomUUID().toString(), UUID.randomUUID().toString(), User.Source.LDAP); response.setAdminId(String.valueOf(userAccount.getAccountId())); - logger.info("created an account with name " + admin + " in the given domain " + domainId); + logger.info("created an account with name {} in the given domain {} with id {}", admin, _domainService.getDomain(domainId), domainId); } catch (Exception e) { - logger.info("an exception occurred while creating account with name " + admin +" in domain " + domainId, e); + logger.info("an exception occurred while creating account with name {} in domain {} with id {}", admin, _domainService.getDomain(domainId), domainId, e); } } else { - logger.debug("an account with name " + admin + " already exists in the domain " + domainId); + logger.debug("an account with name {} already exists in the domain {} with id {}", admin, _domainService.getDomain(domainId), domainId); } } else { - logger.debug("ldap user with username "+admin+" is disabled in the given group/ou"); + logger.debug("ldap user with username {} is disabled in the given Group/OU", admin); } } response.setObjectName("LinkDomainToLdap"); diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/UnlinkDomainFromLdapCmd.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/UnlinkDomainFromLdapCmd.java new file mode 100644 index 000000000000..08f5e99bc2de --- /dev/null +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/command/UnlinkDomainFromLdapCmd.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cloudstack.api.command; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.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.DomainResponse; +import org.apache.cloudstack.ldap.LdapManager; + +import javax.inject.Inject; + +@APICommand(name = "unlinkDomainFromLdap", description = "remove the linkage of a Domain to a group or OU in ldap", + responseObject = SuccessResponse.class, since = "4.23.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class UnlinkDomainFromLdapCmd extends BaseCmd { + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, required = true, entityType = DomainResponse.class, + description = "The ID of the Domain which has to be unlinked from LDAP.") + private Long domainId; + + @Inject + private LdapManager _ldapManager; + + public Long getDomainId() { + return domainId; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + boolean rc = _ldapManager.unlinkDomainFromLdap(this); + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setSuccess(rc); + if (rc) { + response.setDisplayText("Domain unlinked from LDAP successfully"); + } else { + response.setDisplayText("Failed to unlink domain from LDAP"); + } + setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/response/LdapConfigurationResponse.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/response/LdapConfigurationResponse.java index 744c73d8e774..b9da4f84d172 100644 --- a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/response/LdapConfigurationResponse.java +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/response/LdapConfigurationResponse.java @@ -23,20 +23,24 @@ import com.cloud.serializer.Param; import org.apache.cloudstack.api.EntityReference; -import org.apache.cloudstack.ldap.LdapConfiguration; +import org.apache.cloudstack.ldap.LdapConfigurationVO; -@EntityReference(value = LdapConfiguration.class) +@EntityReference(value = LdapConfigurationVO.class) public class LdapConfigurationResponse extends BaseResponse { + @SerializedName("id") + @Param(description = "the ID of the LDAP configuration") + private String id; + @SerializedName(ApiConstants.HOST_NAME) - @Param(description = "name of the host running the ldap server") + @Param(description = "Name of the host running the LDAP server") private String hostname; @SerializedName(ApiConstants.PORT) - @Param(description = "port the ldap server is running on") + @Param(description = "Port the LDAP server is running on") private int port; @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "linked domain") + @Param(description = "Linked domain") private String domainId; public LdapConfigurationResponse() { @@ -53,9 +57,18 @@ public LdapConfigurationResponse(final String hostname, final int port) { setPort(port); } - public LdapConfigurationResponse(final String hostname, final int port, final String domainId) { + public LdapConfigurationResponse(final String hostname, final int port, final String domainId, final String id) { this(hostname, port); setDomainId(domainId); + setId(id); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; } public String getHostname() { diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/response/LinkAccountToLdapResponse.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/response/LinkAccountToLdapResponse.java index 6273273bdbff..2059c9e41bcc 100644 --- a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/response/LinkAccountToLdapResponse.java +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/response/LinkAccountToLdapResponse.java @@ -26,15 +26,15 @@ public class LinkAccountToLdapResponse extends BaseResponse { @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "id of the Domain which is linked to LDAP") + @Param(description = "ID of the Domain which is linked to LDAP") private String domainId; @SerializedName(ApiConstants.LDAP_DOMAIN) - @Param(description = "name of the group or OU in LDAP which is linked to the domain") + @Param(description = "Name of the group or OU in LDAP which is linked to the domain") private String ldapDomain; @SerializedName(ApiConstants.TYPE) - @Param(description = "type of the name in LDAP which is linked to the domain") + @Param(description = "Type of the name in LDAP which is linked to the domain") private String type; @SerializedName(ApiConstants.ACCOUNT_TYPE) @@ -46,7 +46,7 @@ public class LinkAccountToLdapResponse extends BaseResponse { private String adminId; @SerializedName(ApiConstants.ACCOUNT) - @Param(description = "name of the account") + @Param(description = "Name of the Account") private String accountName; @@ -82,4 +82,8 @@ public String getAdminId() { public void setAdminId(String adminId) { this.adminId = adminId; } + + public String getAccountName() { + return accountName; + } } diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/response/LinkDomainToLdapResponse.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/response/LinkDomainToLdapResponse.java index be057fd84180..2a4c764bda63 100644 --- a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/response/LinkDomainToLdapResponse.java +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/api/response/LinkDomainToLdapResponse.java @@ -26,24 +26,24 @@ public class LinkDomainToLdapResponse extends BaseResponse { @SerializedName(ApiConstants.DOMAIN_ID) - @Param(description = "id of the Domain which is linked to LDAP") + @Param(description = "ID of the Domain which is linked to LDAP") private String domainId; @Deprecated @SerializedName(ApiConstants.NAME) - @Param(description = "name of the group or OU in LDAP which is linked to the domain") + @Param(description = "Name of the group or OU in LDAP which is linked to the domain") private String name; @SerializedName(ApiConstants.LDAP_DOMAIN) - @Param(description = "name of the group or OU in LDAP which is linked to the domain") + @Param(description = "Name of the group or OU in LDAP which is linked to the domain") private String ldapDomain; @SerializedName(ApiConstants.TYPE) - @Param(description = "type of the name in LDAP which is linked to the domain") + @Param(description = "Type of the name in LDAP which is linked to the domain") private String type; @SerializedName(ApiConstants.ACCOUNT_TYPE) - @Param(description = "Type of the account to auto import") + @Param(description = "Type of the Account to auto import") private int accountType; @SerializedName(ApiConstants.ACCOUNT_ID) diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/ADLdapUserManagerImpl.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/ADLdapUserManagerImpl.java index 552d5969a9e4..3f8aa6a4c514 100644 --- a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/ADLdapUserManagerImpl.java +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/ADLdapUserManagerImpl.java @@ -39,7 +39,7 @@ public List getUsersInGroup(String groupName, LdapContext context, Lon throw new IllegalArgumentException("ldap group name cannot be blank"); } - String basedn = _ldapConfiguration.getBaseDn(domainId); + String basedn = LdapConfiguration.getBaseDn(domainId); if (StringUtils.isBlank(basedn)) { throw new IllegalArgumentException("ldap basedn is not configured"); } @@ -49,7 +49,7 @@ public List getUsersInGroup(String groupName, LdapContext context, Lon searchControls.setReturningAttributes(_ldapConfiguration.getReturnAttributes(domainId)); NamingEnumeration results = context.search(basedn, generateADGroupSearchFilter(groupName, domainId), searchControls); - final List users = new ArrayList(); + final List users = new ArrayList<>(); while (results.hasMoreElements()) { final SearchResult result = results.nextElement(); users.add(createUser(result, domainId)); @@ -58,13 +58,11 @@ public List getUsersInGroup(String groupName, LdapContext context, Lon } String generateADGroupSearchFilter(String groupName, Long domainId) { - final StringBuilder userObjectFilter = new StringBuilder(); - userObjectFilter.append("(objectClass="); - userObjectFilter.append(_ldapConfiguration.getUserObject(domainId)); - userObjectFilter.append(")"); + + final StringBuilder userObjectFilter = getUserObjectFilter(domainId); final StringBuilder memberOfFilter = new StringBuilder(); - String groupCnName = _ldapConfiguration.getCommonNameAttribute() + "=" +groupName + "," + _ldapConfiguration.getBaseDn(domainId); + String groupCnName = LdapConfiguration.getCommonNameAttribute() + "=" +groupName + "," + LdapConfiguration.getBaseDn(domainId); memberOfFilter.append("(").append(getMemberOfAttribute(domainId)).append("="); memberOfFilter.append(groupCnName); memberOfFilter.append(")"); @@ -75,10 +73,18 @@ String generateADGroupSearchFilter(String groupName, Long domainId) { result.append(memberOfFilter); result.append(")"); - logger.debug("group search filter = " + result); + logger.debug("group search filter = {}", result); return result.toString(); } + StringBuilder getUserObjectFilter(Long domainId) { + final StringBuilder userObjectFilter = new StringBuilder(); + userObjectFilter.append("(&(objectCategory=person)"); + userObjectFilter.append(super.getUserObjectFilter(domainId)); + userObjectFilter.append(")"); + return userObjectFilter; + } + protected boolean isUserDisabled(SearchResult result) throws NamingException { boolean isDisabledUser = false; String userAccountControl = LdapUtils.getAttributeValue(result.getAttributes(), _ldapConfiguration.getUserAccountControlAttribute()); @@ -93,10 +99,14 @@ protected boolean isUserDisabled(SearchResult result) throws NamingException { } protected String getMemberOfAttribute(final Long domainId) { - if(_ldapConfiguration.isNestedGroupsEnabled(domainId)) { - return MICROSOFT_AD_NESTED_MEMBERS_FILTER; + String rc; + if(LdapConfiguration.isNestedGroupsEnabled(domainId)) { + rc = MICROSOFT_AD_NESTED_MEMBERS_FILTER; } else { - return MICROSOFT_AD_MEMBERS_FILTER; + rc = MICROSOFT_AD_MEMBERS_FILTER; } + logger.trace("using memberOf filter = {} for domain with id {}", rc, domainId); + + return rc; } } diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapAuthenticator.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapAuthenticator.java index b85098815943..09519c5641cb 100644 --- a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapAuthenticator.java +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapAuthenticator.java @@ -25,6 +25,7 @@ import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.auth.UserAuthenticator; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import com.cloud.user.Account; @@ -45,6 +46,8 @@ public class LdapAuthenticator extends AdapterBase implements UserAuthenticator @Inject private AccountManager _accountManager; + private static final String LDAP_READ_TIMED_OUT_MESSAGE = "LDAP response read timed out"; + public LdapAuthenticator() { super(); } @@ -57,10 +60,10 @@ public LdapAuthenticator(final LdapManager ldapManager, final UserAccountDao use @Override public Pair authenticate(final String username, final String password, final Long domainId, final Map requestParameters) { - Pair rc = new Pair(false, null); + Pair rc = new Pair<>(false, null); if (logger.isDebugEnabled()) { - logger.debug("Retrieving ldap user: " + username); + logger.debug("Retrieving ldap user: {}", username); } // TODO not allowing an empty password is a policy we shouldn't decide on. A private cloud may well want to allow this. @@ -74,8 +77,8 @@ public Pair authenticate(final String use return rc; } List ldapTrustMapVOs = getLdapTrustMapVOS(domainId); - if(ldapTrustMapVOs != null && ldapTrustMapVOs.size() > 0) { - if(ldapTrustMapVOs.size() == 1 && ldapTrustMapVOs.get(0).getAccountId() == 0) { + if (CollectionUtils.isNotEmpty(ldapTrustMapVOs)) { + if (ldapTrustMapVOs.size() == 1 && ldapTrustMapVOs.get(0).getAccountId() == 0) { if (logger.isTraceEnabled()) { logger.trace("We have a single mapping of a domain to an ldap group or ou"); } @@ -88,7 +91,7 @@ public Pair authenticate(final String use } } else { if (logger.isTraceEnabled()) { - logger.trace(String.format("'this' domain (%d) is not linked to ldap follow normal authentication", domainId)); + logger.trace("'this' domain ({}) is not linked to LDAP follow normal authentication", domainId); } rc = authenticate(username, password, domainId, user); } @@ -112,10 +115,10 @@ private List getLdapTrustMapVOS(Long domainId) { * @param domainId domain the user is trying to log on to * @param userAccount cloudstack user object * @param ldapTrustMapVOs the trust mappings of accounts in the domain to ldap groups - * @return false if the ldap user object does not exist, is not mapped to an account, is mapped to multiple accounts or if authenitication fails + * @return {false,} if the ldap user object does not exist, is not mapped to an account, is mapped to multiple accounts or if authentication fails */ Pair authenticate(String username, String password, Long domainId, UserAccount userAccount, List ldapTrustMapVOs) { - Pair rc = new Pair(false, null); + Pair rc = new Pair<>(false, null); try { LdapUser ldapUser = _ldapManager.getUser(username, domainId); List memberships = ldapUser.getMemberships(); @@ -125,11 +128,11 @@ Pair authenticate(String username, String mappedGroups.retainAll(memberships); tracelist("actual groups for " + username, mappedGroups); // check membership, there must be only one match in this domain - if(ldapUser.isDisabled()) { + if (ldapUser.isDisabled()) { logAndDisable(userAccount, "attempt to log on using disabled ldap user " + userAccount.getUsername(), false); - } else if(mappedGroups.size() > 1) { + } else if (mappedGroups.size() > 1) { logAndDisable(userAccount, "user '" + username + "' is mapped to more then one account in domain and will be disabled.", false); - } else if(mappedGroups.size() < 1) { + } else if (mappedGroups.isEmpty()) { logAndDisable(userAccount, "user '" + username + "' is not mapped to an account in domain and will be removed.", true); } else { // a valid ldap configured user exists @@ -137,12 +140,12 @@ Pair authenticate(String username, String // we could now assert that ldapTrustMapVOs.contains(mapping); // createUser in Account can only be done by account name not by account id; Account account = _accountManager.getAccount(mapping.getAccountId()); - if(null == account) { + if (null == account) { throw new CloudRuntimeException(String.format("account for user (%s) not found by id %d", username, mapping.getAccountId())); } String accountName = account.getAccountName(); rc.first(_ldapManager.canAuthenticate(ldapUser.getPrincipal(), password, domainId)); - if (! rc.first()) { + if (!rc.first()) { rc.second(ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT); } // for security reasons we keep processing on faulty login attempt to not give a way information on userid existence @@ -162,7 +165,7 @@ Pair authenticate(String username, String userAccount = _accountManager.getUserAccountById(user.getId()); } else { // not a new user, check if mapped group has changed - if(userAccount.getAccountId() != mapping.getAccountId()) { + if (userAccount.getAccountId() != mapping.getAccountId()) { final Account mappedAccount = _accountManager.getAccount(mapping.getAccountId()); if (mappedAccount == null || mappedAccount.getRemoved() != null) { throw new CloudRuntimeException("Mapped account for users does not exist. Please contact your administrator."); @@ -174,12 +177,21 @@ Pair authenticate(String username, String } } catch (NoLdapUserMatchingQueryException e) { logger.debug(e.getMessage()); - disableUserInCloudStack(userAccount); + processLdapUserErrorMessage(userAccount, e.getMessage(), rc); } return rc; } + private void processLdapUserErrorMessage(UserAccount user, String errorMessage, Pair rc) { + if (StringUtils.isNotEmpty(errorMessage) && errorMessage.contains(LDAP_READ_TIMED_OUT_MESSAGE) && !rc.first()) { + rc.second(ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT); + } else { + // no user in ldap ==>> disable user in cloudstack + disableUserInCloudStack(user); + } + } + private void tracelist(String msg, List listToTrace) { if (logger.isTraceEnabled()) { StringBuilder logMsg = new StringBuilder(); @@ -197,7 +209,7 @@ private void logAndDisable(UserAccount userAccount, String msg, boolean remove) if (logger.isInfoEnabled()) { logger.info(msg); } - if(remove) { + if (remove) { removeUserInCloudStack(userAccount); } else { disableUserInCloudStack(userAccount); @@ -219,33 +231,32 @@ List getMappedGroups(List ldapTrustMapVOs) { * @param domainId domain the user is trying to log on to * @param user cloudstack user object * @param ldapTrustMapVO the trust mapping for the domain to the ldap group - * @return false if the ldap user object does not exist or authenitication fails + * @return {false, } if the ldap user object does not exist or authentication fails */ private Pair authenticate(String username, String password, Long domainId, UserAccount user, LdapTrustMapVO ldapTrustMapVO) { - Pair rc = new Pair(false, null); + Pair rc = new Pair<>(false, null); try { LdapUser ldapUser = _ldapManager.getUser(username, ldapTrustMapVO.getType().toString(), ldapTrustMapVO.getName(), domainId); final Account.Type accountType = ldapTrustMapVO.getAccountType(); processLdapUser(password, domainId, user, rc, ldapUser, accountType); } catch (NoLdapUserMatchingQueryException e) { logger.debug(e.getMessage()); - // no user in ldap ==>> disable user in cloudstack - disableUserInCloudStack(user); + processLdapUserErrorMessage(user, e.getMessage(), rc); } return rc; } private void processLdapUser(String password, Long domainId, UserAccount user, Pair rc, LdapUser ldapUser, Account.Type accountType) { - if(!ldapUser.isDisabled()) { + if (!ldapUser.isDisabled()) { rc.first(_ldapManager.canAuthenticate(ldapUser.getPrincipal(), password, domainId)); - if(rc.first()) { - if(user == null) { + if (rc.first()) { + if (user == null) { // import user to cloudstack createCloudStackUserAccount(ldapUser, domainId, accountType); } else { enableUserInCloudStack(user); } - } else if(user != null) { + } else if (user != null) { rc.second(ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT); } } else { @@ -260,34 +271,38 @@ private void processLdapUser(String password, Long domainId, UserAccount user, P * @param password pass phrase * @param domainId domain the user is trying to log on to * @param user cloudstack user object - * @return false if either user object does not exist or authenitication fails + * @return {false,} if either user object does not exist or authentication fails */ Pair authenticate(String username, String password, Long domainId, UserAccount user) { boolean result = false; + boolean timedOut = false; - if(user != null ) { + if (user != null ) { try { LdapUser ldapUser = _ldapManager.getUser(username, domainId); - if(!ldapUser.isDisabled()) { + if (!ldapUser.isDisabled()) { result = _ldapManager.canAuthenticate(ldapUser.getPrincipal(), password, domainId); } else { - logger.debug("user with principal "+ ldapUser.getPrincipal() + " is disabled in ldap"); + logger.debug("user with principal {} is disabled in ldap", () -> ldapUser.getPrincipal()); } } catch (NoLdapUserMatchingQueryException e) { logger.debug(e.getMessage()); + if (e.getMessage().contains(LDAP_READ_TIMED_OUT_MESSAGE)) { + timedOut = true; + } } } - return processResultAndAction(user, result); + return processResultAndAction(user, result, timedOut); } - private Pair processResultAndAction(UserAccount user, boolean result) { - return (!result && user != null) ? - new Pair(result, ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT): - new Pair(result, null); + private Pair processResultAndAction(UserAccount user, boolean result, boolean timedOut) { + return (!result && (user != null || timedOut)) ? + new Pair<>(false, ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT): + new Pair<>(result, null); } private void enableUserInCloudStack(UserAccount user) { - if(user != null && (user.getState().equalsIgnoreCase(Account.State.DISABLED.toString()))) { + if (user != null && (user.getState().equalsIgnoreCase(Account.State.DISABLED.toString()))) { _accountManager.enableUser(user.getId()); } } diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapConfiguration.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapConfiguration.java index 6a62ad8d99df..114ca1631ec5 100644 --- a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapConfiguration.java +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapConfiguration.java @@ -23,15 +23,17 @@ import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; -import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import com.cloud.utils.Pair; import org.apache.cloudstack.ldap.dao.LdapConfigurationDao; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public class LdapConfiguration implements Configurable{ private final static String factory = "com.sun.jndi.ldap.LdapCtxFactory"; + protected Logger logger = LogManager.getLogger(getClass()); - private static final ConfigKey ldapReadTimeout = new ConfigKey( + private static final ConfigKey ldapReadTimeout = new ConfigKey<>( Long.class, "ldap.read.timeout", "Advanced", @@ -39,9 +41,9 @@ public class LdapConfiguration implements Configurable{ "LDAP connection Timeout in milli sec", true, ConfigKey.Scope.Domain, - 1l); + 1L); - private static final ConfigKey ldapPageSize = new ConfigKey( + private static final ConfigKey ldapPageSize = new ConfigKey<>( Integer.class, "ldap.request.page.size", "Advanced", @@ -51,7 +53,7 @@ public class LdapConfiguration implements Configurable{ ConfigKey.Scope.Domain, 1); - private static final ConfigKey ldapEnableNestedGroups = new ConfigKey( + private static final ConfigKey ldapEnableNestedGroups = new ConfigKey<>( "Advanced", Boolean.class, "ldap.nested.groups.enable", @@ -60,7 +62,7 @@ public class LdapConfiguration implements Configurable{ true, ConfigKey.Scope.Domain); - private static final ConfigKey ldapMemberOfAttribute = new ConfigKey( + private static final ConfigKey ldapMemberOfAttribute = new ConfigKey<>( "Advanced", String.class, "ldap.user.memberof.attribute", @@ -69,7 +71,7 @@ public class LdapConfiguration implements Configurable{ true, ConfigKey.Scope.Domain); - private static final ConfigKey ldapProvider = new ConfigKey( + private static final ConfigKey ldapProvider = new ConfigKey<>( "Advanced", String.class, "ldap.provider", @@ -78,7 +80,7 @@ public class LdapConfiguration implements Configurable{ true, ConfigKey.Scope.Domain); - private static final ConfigKey ldapBaseDn = new ConfigKey( + private static final ConfigKey ldapBaseDn = new ConfigKey<>( "Advanced", String.class, "ldap.basedn", @@ -87,7 +89,7 @@ public class LdapConfiguration implements Configurable{ true, ConfigKey.Scope.Domain); - private static final ConfigKey ldapBindPassword = new ConfigKey( + private static final ConfigKey ldapBindPassword = new ConfigKey<>( "Secure", String.class, "ldap.bind.password", @@ -95,7 +97,7 @@ public class LdapConfiguration implements Configurable{ "Sets the bind password for LDAP", true, ConfigKey.Scope.Domain); - private static final ConfigKey ldapBindPrincipal = new ConfigKey( + private static final ConfigKey ldapBindPrincipal = new ConfigKey<>( "Secure", String.class, "ldap.bind.principal", @@ -103,7 +105,7 @@ public class LdapConfiguration implements Configurable{ "Sets the bind principal for LDAP", true, ConfigKey.Scope.Domain); - private static final ConfigKey ldapEmailAttribute = new ConfigKey( + private static final ConfigKey ldapEmailAttribute = new ConfigKey<>( "Advanced", String.class, "ldap.email.attribute", @@ -111,7 +113,7 @@ public class LdapConfiguration implements Configurable{ "Sets the email attribute used within LDAP", true, ConfigKey.Scope.Domain); - private static final ConfigKey ldapFirstnameAttribute = new ConfigKey( + private static final ConfigKey ldapFirstnameAttribute = new ConfigKey<>( "Advanced", String.class, "ldap.firstname.attribute", @@ -119,14 +121,14 @@ public class LdapConfiguration implements Configurable{ "Sets the firstname attribute used within LDAP", true, ConfigKey.Scope.Domain); - private static final ConfigKey ldapLastnameAttribute = new ConfigKey( + private static final ConfigKey ldapLastnameAttribute = new ConfigKey<>( "Advanced", String.class, "ldap.lastname.attribute", "sn", "Sets the lastname attribute used within LDAP", true, ConfigKey.Scope.Domain); - private static final ConfigKey ldapUsernameAttribute = new ConfigKey( + private static final ConfigKey ldapUsernameAttribute = new ConfigKey<>( "Advanced", String.class, "ldap.username.attribute", @@ -134,7 +136,7 @@ public class LdapConfiguration implements Configurable{ "Sets the username attribute used within LDAP", true, ConfigKey.Scope.Domain); - private static final ConfigKey ldapUserObject = new ConfigKey( + private static final ConfigKey ldapUserObject = new ConfigKey<>( "Advanced", String.class, "ldap.user.object", @@ -142,7 +144,7 @@ public class LdapConfiguration implements Configurable{ "Sets the object type of users within LDAP", true, ConfigKey.Scope.Domain); - private static final ConfigKey ldapSearchGroupPrinciple = new ConfigKey( + private static final ConfigKey ldapSearchGroupPrinciple = new ConfigKey<>( "Advanced", String.class, "ldap.search.group.principle", @@ -150,7 +152,7 @@ public class LdapConfiguration implements Configurable{ "Sets the principle of the group that users must be a member of", true, ConfigKey.Scope.Domain); - private static final ConfigKey ldapGroupObject = new ConfigKey( + private static final ConfigKey ldapGroupObject = new ConfigKey<>( "Advanced", String.class, "ldap.group.object", @@ -158,7 +160,7 @@ public class LdapConfiguration implements Configurable{ "Sets the object type of groups within LDAP", true, ConfigKey.Scope.Domain); - private static final ConfigKey ldapGroupUniqueMemberAttribute = new ConfigKey( + private static final ConfigKey ldapGroupUniqueMemberAttribute = new ConfigKey<>( "Advanced", String.class, "ldap.group.user.uniquemember", @@ -167,7 +169,7 @@ public class LdapConfiguration implements Configurable{ true, ConfigKey.Scope.Domain); - private static final ConfigKey ldapTrustStore = new ConfigKey( + private static final ConfigKey ldapTrustStore = new ConfigKey<>( "Advanced", String.class, "ldap.truststore", @@ -175,7 +177,7 @@ public class LdapConfiguration implements Configurable{ "Sets the path to the truststore to use for SSL", true, ConfigKey.Scope.Domain); - private static final ConfigKey ldapTrustStorePassword = new ConfigKey( + private static final ConfigKey ldapTrustStorePassword = new ConfigKey<>( "Secure", String.class, "ldap.truststore.password", @@ -196,11 +198,6 @@ public LdapConfiguration(final LdapConfigurationDao ldapConfigurationDao) { _ldapConfigurationDao = ldapConfigurationDao; } - @Deprecated - public LdapConfiguration(final ConfigurationDao configDao, final LdapConfigurationDao ldapConfigurationDao) { - _ldapConfigurationDao = ldapConfigurationDao; - } - public String getAuthentication(final Long domainId) { if ((getBindPrincipal(domainId) == null) && (getBindPassword(domainId) == null)) { return "none"; @@ -209,36 +206,36 @@ public String getAuthentication(final Long domainId) { } } - public String getBaseDn(final Long domainId) { + public static String getBaseDn(final Long domainId) { return ldapBaseDn.valueIn(domainId); } - public String getBindPassword(final Long domainId) { + public static String getBindPassword(final Long domainId) { return ldapBindPassword.valueIn(domainId); } - public String getBindPrincipal(final Long domainId) { + public static String getBindPrincipal(final Long domainId) { return ldapBindPrincipal.valueIn(domainId); } - public String getEmailAttribute(final Long domainId) { + public static String getEmailAttribute(final Long domainId) { return ldapEmailAttribute.valueIn(domainId); } - public String getFactory() { + public static String getFactory() { return factory; } - public String getFirstnameAttribute(final Long domainId) { + public static String getFirstnameAttribute(final Long domainId) { return ldapFirstnameAttribute.valueIn(domainId); } - public String getLastnameAttribute(final Long domainId) { + public static String getLastnameAttribute(final Long domainId) { return ldapLastnameAttribute.valueIn(domainId); } public String getProviderUrl(final Long domainId) { - final String protocol = getSSLStatus(domainId) == true ? "ldaps://" : "ldap://"; + final String protocol = getSSLStatus(domainId) ? "ldaps://" : "ldap://"; final Pair, Integer> result = _ldapConfigurationDao.searchConfigurations(null, 0, domainId); final StringBuilder providerUrls = new StringBuilder(); String delim = ""; @@ -270,40 +267,36 @@ public String getSearchGroupPrinciple(final Long domainId) { return ldapSearchGroupPrinciple.valueIn(domainId); } - public boolean getSSLStatus(Long domainId) { - boolean sslStatus = false; - if (getTrustStore(domainId) != null && getTrustStorePassword(domainId) != null) { - sslStatus = true; - } - return sslStatus; + public static boolean getSSLStatus(Long domainId) { + return getTrustStore(domainId) != null && getTrustStorePassword(domainId) != null; } - public String getTrustStore(Long domainId) { + public static String getTrustStore(Long domainId) { return ldapTrustStore.valueIn(domainId); } - public String getTrustStorePassword(Long domainId) { + public static String getTrustStorePassword(Long domainId) { return ldapTrustStorePassword.valueIn(domainId); } - public String getUsernameAttribute(final Long domainId) { + public static String getUsernameAttribute(final Long domainId) { return ldapUsernameAttribute.valueIn(domainId); } - public String getUserObject(final Long domainId) { + public static String getUserObject(final Long domainId) { return ldapUserObject.valueIn(domainId); } - public String getGroupObject(final Long domainId) { + public static String getGroupObject(final Long domainId) { return ldapGroupObject.valueIn(domainId); } - public String getGroupUniqueMemberAttribute(final Long domainId) { + public static String getGroupUniqueMemberAttribute(final Long domainId) { return ldapGroupUniqueMemberAttribute.valueIn(domainId); } // TODO remove hard-coding - public String getCommonNameAttribute() { + public static String getCommonNameAttribute() { return "cn"; } @@ -312,11 +305,11 @@ public String getUserAccountControlAttribute() { return "userAccountControl"; } - public Long getReadTimeout(final Long domainId) { + public static Long getReadTimeout(final Long domainId) { return ldapReadTimeout.valueIn(domainId); } - public Integer getLdapPageSize(final Long domainId) { + public static Integer getLdapPageSize(final Long domainId) { return ldapPageSize.valueIn(domainId); } @@ -325,13 +318,13 @@ public LdapUserManager.Provider getLdapProvider(final Long domainId) { try { provider = LdapUserManager.Provider.valueOf(ldapProvider.valueIn(domainId).toUpperCase()); } catch (IllegalArgumentException ex) { - //openldap is the default + logger.warn("no LDAP provider found for domain {}, using openldap as default", domainId); provider = LdapUserManager.Provider.OPENLDAP; } return provider; } - public boolean isNestedGroupsEnabled(final Long domainId) { + public static boolean isNestedGroupsEnabled(final Long domainId) { return ldapEnableNestedGroups.valueIn(domainId); } diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapConfigurationVO.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapConfigurationVO.java index ee9f0930c47f..7e51fe352d96 100644 --- a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapConfigurationVO.java +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapConfigurationVO.java @@ -23,19 +23,25 @@ import javax.persistence.Id; import javax.persistence.Table; +import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; +import java.util.UUID; + @Entity @Table(name = "ldap_configuration") -public class LdapConfigurationVO implements InternalIdentity { - @Column(name = "hostname") - private String hostname; - +public class LdapConfigurationVO implements Identity, InternalIdentity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Long id; + @Column(name = "hostname") + private String hostname; + + @Column(name = "uuid") + private String uuid; + @Column(name = "port") private int port; @@ -43,12 +49,14 @@ public class LdapConfigurationVO implements InternalIdentity { private Long domainId; public LdapConfigurationVO() { + this.uuid = UUID.randomUUID().toString(); } public LdapConfigurationVO(final String hostname, final int port, final Long domainId) { this.hostname = hostname; this.port = port; this.domainId = domainId; + this.uuid = UUID.randomUUID().toString(); } public String getHostname() { @@ -60,6 +68,10 @@ public long getId() { return id; } + public String getUuid() { + return uuid; + } + public int getPort() { return port; } diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapContextFactory.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapContextFactory.java index e6f23ef8ab3f..34ebc7a44991 100644 --- a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapContextFactory.java +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapContextFactory.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.ldap; +import java.io.FileInputStream; import java.io.IOException; import java.util.Hashtable; @@ -24,6 +25,7 @@ import javax.naming.NamingException; import javax.naming.ldap.InitialLdapContext; import javax.naming.ldap.LdapContext; +import java.security.KeyStore; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; @@ -47,19 +49,19 @@ public LdapContext createBindContext(Long domainId) throws NamingException, IOEx } public LdapContext createBindContext(final String providerUrl, Long domainId) throws NamingException, IOException { - final String bindPrincipal = _ldapConfiguration.getBindPrincipal(domainId); - final String bindPassword = _ldapConfiguration.getBindPassword(domainId); + final String bindPrincipal = LdapConfiguration.getBindPrincipal(domainId); + final String bindPassword = LdapConfiguration.getBindPassword(domainId); return createInitialDirContext(bindPrincipal, bindPassword, providerUrl, true, domainId); } - private LdapContext createInitialDirContext(final String principal, final String password, final boolean isSystemContext, Long domainId) throws NamingException, IOException { + private LdapContext createInitialDirContext(final String principal, final String password, final boolean isSystemContext, Long domainId) throws NamingException { return createInitialDirContext(principal, password, null, isSystemContext, domainId); } private LdapContext createInitialDirContext(final String principal, final String password, final String providerUrl, final boolean isSystemContext, Long domainId) - throws NamingException, IOException { + throws NamingException { Hashtable environment = getEnvironment(principal, password, providerUrl, isSystemContext, domainId); - logger.debug("initializing ldap with provider url: " + environment.get(Context.PROVIDER_URL)); + logger.debug("initializing ldap with provider url: {}", environment.get(Context.PROVIDER_URL)); return new InitialLdapContext(environment, null); } @@ -68,18 +70,46 @@ public LdapContext createUserContext(final String principal, final String passwo } private void enableSSL(final Hashtable environment, Long domainId) { - final boolean sslStatus = _ldapConfiguration.getSSLStatus(domainId); + final boolean sslStatus = LdapConfiguration.getSSLStatus(domainId); if (sslStatus) { logger.info("LDAP SSL enabled."); environment.put(Context.SECURITY_PROTOCOL, "ssl"); - System.setProperty("javax.net.ssl.trustStore", _ldapConfiguration.getTrustStore(domainId)); - System.setProperty("javax.net.ssl.trustStorePassword", _ldapConfiguration.getTrustStorePassword(domainId)); + String trustStore = LdapConfiguration.getTrustStore(domainId); + String trustStorePassword = LdapConfiguration.getTrustStorePassword(domainId); + + if (!validateTrustStore(trustStore, trustStorePassword)) { + throw new RuntimeException("Invalid truststore or truststore password"); + } + + System.setProperty("javax.net.ssl.trustStore", trustStore); + System.setProperty("javax.net.ssl.trustStorePassword", trustStorePassword); + } + } + + private boolean validateTrustStore(String trustStore, String trustStorePassword) { + if (trustStore == null) { + return true; + } + + if (trustStorePassword == null) { + return false; + } + + try { + KeyStore.getInstance("JKS").load( + new FileInputStream(trustStore), + trustStorePassword.toCharArray() + ); + return true; + } catch (Exception e) { + logger.warn("Failed to validate truststore: {}", e.getMessage()); + return false; } } private Hashtable getEnvironment(final String principal, final String password, final String providerUrl, final boolean isSystemContext, Long domainId) { - final String factory = _ldapConfiguration.getFactory(); + final String factory = LdapConfiguration.getFactory(); String url = providerUrl == null ? _ldapConfiguration.getProviderUrl(domainId) : providerUrl; if (StringUtils.isEmpty(url) && domainId != null) { //try a default ldap implementation @@ -90,7 +120,7 @@ private Hashtable getEnvironment(final String principal, final S environment.put(Context.INITIAL_CONTEXT_FACTORY, factory); environment.put(Context.PROVIDER_URL, url); - environment.put("com.sun.jndi.ldap.read.timeout", _ldapConfiguration.getReadTimeout(domainId).toString()); + environment.put("com.sun.jndi.ldap.read.timeout", LdapConfiguration.getReadTimeout(domainId).toString()); environment.put("com.sun.jndi.ldap.connect.pool", "true"); enableSSL(environment, domainId); diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapManager.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapManager.java index ded6e94c12a8..7e561ccf754a 100644 --- a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapManager.java +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapManager.java @@ -23,6 +23,7 @@ import org.apache.cloudstack.api.command.LdapListConfigurationCmd; import org.apache.cloudstack.api.command.LinkAccountToLdapCmd; import org.apache.cloudstack.api.command.LinkDomainToLdapCmd; +import org.apache.cloudstack.api.command.UnlinkDomainFromLdapCmd; import org.apache.cloudstack.api.response.LdapConfigurationResponse; import org.apache.cloudstack.api.response.LdapUserResponse; @@ -34,7 +35,7 @@ public interface LdapManager extends PluggableService { - enum LinkType { GROUP, OU;} + enum LinkType { GROUP, OU } LdapConfigurationResponse addConfiguration(final LdapAddConfigurationCmd cmd) throws InvalidParameterValueException; @@ -69,12 +70,12 @@ enum LinkType { GROUP, OU;} LinkDomainToLdapResponse linkDomainToLdap(LinkDomainToLdapCmd cmd); + boolean unlinkDomainFromLdap(UnlinkDomainFromLdapCmd cmd); + LdapTrustMapVO getDomainLinkedToLdap(long domainId); List getDomainLinkage(long domainId); - LdapTrustMapVO getAccountLinkedToLdap(long domainId, long accountId); - LdapTrustMapVO getLinkedLdapGroup(long domainId, String group); LinkAccountToLdapResponse linkAccountToLdap(LinkAccountToLdapCmd linkAccountToLdapCmd); diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapManagerImpl.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapManagerImpl.java index 68f5580ed1be..8327072639c7 100644 --- a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapManagerImpl.java +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapManagerImpl.java @@ -43,15 +43,17 @@ import org.apache.cloudstack.api.command.LdapUserSearchCmd; import org.apache.cloudstack.api.command.LinkAccountToLdapCmd; import org.apache.cloudstack.api.command.LinkDomainToLdapCmd; +import org.apache.cloudstack.api.command.UnlinkDomainFromLdapCmd; import org.apache.cloudstack.api.response.LdapConfigurationResponse; import org.apache.cloudstack.api.response.LdapUserResponse; import org.apache.cloudstack.api.response.LinkAccountToLdapResponse; import org.apache.cloudstack.api.response.LinkDomainToLdapResponse; import org.apache.cloudstack.framework.messagebus.MessageBus; -import org.apache.cloudstack.framework.messagebus.MessageSubscriber; import org.apache.cloudstack.ldap.dao.LdapConfigurationDao; import org.apache.cloudstack.ldap.dao.LdapTrustMapDao; import org.apache.commons.lang.Validate; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; import org.springframework.stereotype.Component; import com.cloud.domain.DomainVO; @@ -113,36 +115,30 @@ public boolean configure(String name, Map params) throws Configu } private void addAccountRemovalListener() { - messageBus.subscribe(AccountManager.MESSAGE_REMOVE_ACCOUNT_EVENT, new MessageSubscriber() { - @Override - public void onPublishMessage(String senderAddress, String subject, Object args) { - try { - final Account account = accountDao.findByIdIncludingRemoved((Long) args); - long domainId = account.getDomainId(); - LdapTrustMapVO ldapTrustMapVO = _ldapTrustMapDao.findByAccount(domainId, account.getAccountId()); - if (ldapTrustMapVO != null) { - removeTrustmap(ldapTrustMapVO); - } - } catch (final Exception e) { - logger.error("Caught exception while removing account linked to LDAP", e); + messageBus.subscribe(AccountManager.MESSAGE_REMOVE_ACCOUNT_EVENT, (senderAddress, subject, args) -> { + try { + final Account account = accountDao.findByIdIncludingRemoved((Long) args); + long domainId = account.getDomainId(); + LdapTrustMapVO ldapTrustMapVO = _ldapTrustMapDao.findByAccount(domainId, account.getAccountId()); + if (ldapTrustMapVO != null) { + removeTrustmap(ldapTrustMapVO); } + } catch (final Exception e) { + logger.error("Caught exception while removing account linked to LDAP", e); } }); } private void addDomainRemovalListener() { - messageBus.subscribe(DomainManager.MESSAGE_REMOVE_DOMAIN_EVENT, new MessageSubscriber() { - @Override - public void onPublishMessage(String senderAddress, String subject, Object args) { - try { - long domainId = ((DomainVO) args).getId(); - List ldapTrustMapVOs = _ldapTrustMapDao.searchByDomainId(domainId); - for (LdapTrustMapVO ldapTrustMapVO : ldapTrustMapVOs) { - removeTrustmap(ldapTrustMapVO); - } - } catch (final Exception e) { - logger.error("Caught exception while removing trust-map for domain linked to LDAP", e); + messageBus.subscribe(DomainManager.MESSAGE_REMOVE_DOMAIN_EVENT, (senderAddress, subject, args) -> { + try { + long domainId = ((DomainVO) args).getId(); + List ldapTrustMapVOs = _ldapTrustMapDao.searchByDomainId(domainId); + for (LdapTrustMapVO ldapTrustMapVO : ldapTrustMapVOs) { + removeTrustmap(ldapTrustMapVO); } + } catch (final Exception e) { + logger.error("Caught exception while removing trust-map for domain linked to LDAP", e); } }); } @@ -166,7 +162,7 @@ public LdapConfigurationResponse addConfiguration(final String hostname, int por private LdapConfigurationResponse addConfigurationInternal(final String hostname, int port, final Long domainId) throws InvalidParameterValueException { // TODO evaluate what the right default should be - if(port <= 0) { + if (port <= 0) { port = 389; } @@ -179,11 +175,16 @@ private LdapConfigurationResponse addConfigurationInternal(final String hostname context = _ldapContextFactory.createBindContext(providerUrl,domainId); configuration = new LdapConfigurationVO(hostname, port, domainId); _ldapConfigurationDao.persist(configuration); - logger.info("Added new ldap server with url: " + providerUrl + (domainId == null ? "": " for domain " + domainId)); + logger.info("Added a new LDAP server with URL: {}{}", providerUrl, domainId == null ? "" : " for domain " + domainId); return createLdapConfigurationResponse(configuration); } catch (NamingException | IOException e) { logger.debug("NamingException while doing an LDAP bind", e); throw new InvalidParameterValueException("Unable to bind to the given LDAP server"); + } catch (RuntimeException e) { + if (e.getMessage().contains("Invalid truststore")) { + throw new InvalidParameterValueException("Invalid truststore or truststore password"); + } + throw e; } finally { closeContext(context); } @@ -194,10 +195,10 @@ private LdapConfigurationResponse addConfigurationInternal(final String hostname /** * TODO decide if the principal is good enough to get the domain id or we need to add it as parameter - * @param principal - * @param password - * @param domainId - * @return + * @param principal ldap user + * @param password the users password to check + * @param domainId the domain for logging into + * @return true if the user can authenticate */ @Override public boolean canAuthenticate(final String principal, final String password, final Long domainId) { @@ -205,13 +206,11 @@ public boolean canAuthenticate(final String principal, final String password, fi // TODO return the right account for this user final LdapContext context = _ldapContextFactory.createUserContext(principal, password, domainId); closeContext(context); - if(logger.isTraceEnabled()) { - logger.trace(String.format("User(%s) authenticated for domain(%s)", principal, domainId)); - } + logger.trace("User({}) authenticated for domain({})", principal, domainId); return true; } catch (NamingException | IOException e) {/* AuthenticationException is caught as NamingException */ - logger.debug("Exception while doing an LDAP bind for user "+" "+principal, e); - logger.info("Failed to authenticate user: " + principal + ". incorrect password."); + logger.debug("Exception while doing an LDAP bind for user {}", principal, e); + logger.info("Failed to authenticate user: {}. Incorrect password.", principal); return false; } } @@ -229,13 +228,13 @@ private void closeContext(final LdapContext context) { @Override public LdapConfigurationResponse createLdapConfigurationResponse(final LdapConfigurationVO configuration) { String domainUuid = null; - if(configuration.getDomainId() != null) { + if (configuration.getDomainId() != null) { DomainVO domain = domainDao.findById(configuration.getDomainId()); if (domain != null) { domainUuid = domain.getUuid(); } } - return new LdapConfigurationResponse(configuration.getHostname(), configuration.getPort(), domainUuid); + return new LdapConfigurationResponse(configuration.getHostname(), configuration.getPort(), domainUuid, configuration.getUuid()); } @Override @@ -252,6 +251,19 @@ public LdapUserResponse createLdapUserResponse(final LdapUser user) { @Override public LdapConfigurationResponse deleteConfiguration(final LdapDeleteConfigurationCmd cmd) throws InvalidParameterValueException { + Long id = cmd.getId(); + String hostname = cmd.getHostname(); + if (id == null && StringUtils.isEmpty(hostname)) { + throw new InvalidParameterValueException("Either id or hostname must be specified"); + } + if (id != null) { + final LdapConfigurationVO config = _ldapConfigurationDao.findById(cmd.getId()); + if (config != null) { + _ldapConfigurationDao.remove(config.getId()); + return createLdapConfigurationResponse(config); + } + throw new InvalidParameterValueException("Cannot find configuration with id " + id); + } return deleteConfigurationInternal(cmd.getHostname(), cmd.getPort(), cmd.getDomainId()); } @@ -266,14 +278,14 @@ private LdapConfigurationResponse deleteConfigurationInternal(final String hostn throw new InvalidParameterValueException("Cannot find configuration with hostname " + hostname); } else { _ldapConfigurationDao.remove(configuration.getId()); - logger.info("Removed ldap server with url: " + hostname + ':' + port + (domainId == null ? "" : " for domain id " + domainId)); + logger.info("Removed ldap server with url: {}:{}{}", hostname, port, domainId == null ? "" : " for domain id " + domainId); return createLdapConfigurationResponse(configuration); } } @Override public List> getCommands() { - final List> cmdList = new ArrayList>(); + final List> cmdList = new ArrayList<>(); cmdList.add(LdapUserSearchCmd.class); cmdList.add(LdapListUsersCmd.class); cmdList.add(LdapAddConfigurationCmd.class); @@ -285,6 +297,7 @@ public List> getCommands() { cmdList.add(LDAPRemoveCmd.class); cmdList.add(LinkDomainToLdapCmd.class); cmdList.add(LinkAccountToLdapCmd.class); + cmdList.add(UnlinkDomainFromLdapCmd.class); return cmdList; } @@ -298,8 +311,8 @@ public LdapUser getUser(final String username, Long domainId) throws NoLdapUserM return _ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider(null)).getUser(escapedUsername, context, domainId); } catch (NamingException | IOException e) { - logger.debug("ldap Exception: ",e); - throw new NoLdapUserMatchingQueryException("No Ldap User found for username: "+username); + logger.debug("LDAP Exception: ", e); + throw new NoLdapUserMatchingQueryException("Unable to find LDAP User for username: " + username + ", due to " + e.getMessage()); } finally { closeContext(context); } @@ -319,8 +332,8 @@ public LdapUser getUser(final String username, final String type, final String n LdapUserManager userManagerFactory = _ldapUserManagerFactory.getInstance(ldapProvider); return userManagerFactory.getUser(escapedUsername, type, name, context, domainId); } catch (NamingException | IOException e) { - logger.debug("ldap Exception: ",e); - throw new NoLdapUserMatchingQueryException("No Ldap User found for username: "+username + " in group: " + name + " of type: " + type); + logger.debug("LDAP Exception: ", e); + throw new NoLdapUserMatchingQueryException("Unable to find LDAP User for username: " + username + " in group: " + name + " of type: " + type + ", due to " + e.getMessage()); } finally { closeContext(context); } @@ -333,7 +346,7 @@ public List getUsers(Long domainId) throws NoLdapUserMatchingQueryExce context = _ldapContextFactory.createBindContext(domainId); return _ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider(domainId)).getUsers(context, domainId); } catch (NamingException | IOException e) { - logger.debug("ldap Exception: ",e); + logger.debug("LDAP Exception: ", e); throw new NoLdapUserMatchingQueryException("*"); } finally { closeContext(context); @@ -347,7 +360,7 @@ public List getUsersInGroup(String groupName, Long domainId) throws No context = _ldapContextFactory.createBindContext(domainId); return _ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider(domainId)).getUsersInGroup(groupName, context, domainId); } catch (NamingException | IOException e) { - logger.debug("ldap NamingException: ",e); + logger.debug("LDAP Exception: ", e); throw new NoLdapUserMatchingQueryException("groupName=" + groupName); } finally { closeContext(context); @@ -372,8 +385,9 @@ public Pair, Integer> listConfigurations(fin final int port = cmd.getPort(); final Long domainId = cmd.getDomainId(); final boolean listAll = cmd.listAll(); - final Pair, Integer> result = _ldapConfigurationDao.searchConfigurations(hostname, port, domainId, listAll); - return new Pair, Integer>(result.first(), result.second()); + final Long id = cmd.getId(); + final Pair, Integer> result = _ldapConfigurationDao.searchConfigurations(id, hostname, port, domainId, listAll); + return new Pair<>(result.first(), result.second()); } @Override @@ -385,7 +399,7 @@ public List searchUsers(final String username) throws NoLdapUserMatchi final String escapedUsername = LdapUtils.escapeLDAPSearchFilter(username); return _ldapUserManagerFactory.getInstance(_ldapConfiguration.getLdapProvider(null)).getUsers("*" + escapedUsername + "*", context, null); } catch (NamingException | IOException e) { - logger.debug("ldap Exception: ",e); + logger.debug("LDAP Exception: ",e); throw new NoLdapUserMatchingQueryException(username); } finally { closeContext(context); @@ -395,7 +409,7 @@ public List searchUsers(final String username) throws NoLdapUserMatchi @Override public LinkDomainToLdapResponse linkDomainToLdap(LinkDomainToLdapCmd cmd) { final Long domainId = cmd.getDomainId(); - final String baseDn = _ldapConfiguration.getBaseDn(domainId); + final String baseDn = LdapConfiguration.getBaseDn(domainId); final String ldapDomain = cmd.getLdapDomain(); Validate.isTrue(baseDn != null, String.format("can not link a domain (with id = %d) unless a basedn (%s) is configured for it.", domainId, baseDn)); @@ -403,6 +417,11 @@ public LinkDomainToLdapResponse linkDomainToLdap(LinkDomainToLdapCmd cmd) { return linkDomainToLdap(cmd.getDomainId(),cmd.getType(), ldapDomain,cmd.getAccountType()); } + @Override + public boolean unlinkDomainFromLdap(UnlinkDomainFromLdapCmd cmd) { + return unlinkDomainFromLdap(cmd.getDomainId()); + } + private LinkDomainToLdapResponse linkDomainToLdap(Long domainId, String type, String name, Account.Type accountType) { Validate.notNull(type, "type cannot be null. It should either be GROUP or OU"); Validate.notNull(domainId, "domainId cannot be null."); @@ -410,16 +429,51 @@ private LinkDomainToLdapResponse linkDomainToLdap(Long domainId, String type, St //Account type should be 0 or 2. check the constants in com.cloud.user.Account Validate.isTrue(accountType== Account.Type.NORMAL || accountType== Account.Type.DOMAIN_ADMIN, "accountype should be either 0(normal user) or 2(domain admin)"); LinkType linkType = LdapManager.LinkType.valueOf(type.toUpperCase()); - LdapTrustMapVO vo = _ldapTrustMapDao.persist(new LdapTrustMapVO(domainId, linkType, name, accountType, 0)); - DomainVO domain = domainDao.findById(vo.getDomainId()); - String domainUuid = ""; + return linkDomainToLdapAndGetResponse(domainId, name, accountType, linkType); + } + + @NotNull + private LinkDomainToLdapResponse linkDomainToLdapAndGetResponse(Long domainId, String name, Account.Type accountType, LinkType linkType) { + DomainVO domain = getDomainToLink(domainId); + LdapTrustMapVO vo = _ldapTrustMapDao.persist(new LdapTrustMapVO(domain.getId(), linkType, name, accountType, 0)); + String domainUuid = domain.getUuid(); + return new LinkDomainToLdapResponse(domainUuid, vo.getType().toString(), vo.getName(), vo.getAccountType().ordinal()); + } + + @NotNull + private DomainVO getDomainToLink(Long domainId) { + DomainVO domain = domainDao.findById(domainId); if (domain == null) { - logger.error("no domain in database for id " + vo.getDomainId()); - } else { - domainUuid = domain.getUuid(); + String msg = "Cannot link Domain to LDAP. No domain found"; + logger.error(msg); + throw new InvalidParameterValueException(msg); } - LinkDomainToLdapResponse response = new LinkDomainToLdapResponse(domainUuid, vo.getType().toString(), vo.getName(), vo.getAccountType().ordinal()); - return response; + return domain; + } + + @NotNull + private LinkAccountToLdapResponse linkAccountToLdapAndGetResponse(LinkAccountToLdapCmd cmd) { + DomainVO domain = getDomainToLink(cmd.getDomainId()); + LinkType linkType = LinkType.valueOf(cmd.getType().toUpperCase()); + Account account = accountDao.findActiveAccount(cmd.getAccountName(), cmd.getDomainId()); + if (account == null) { + account = new AccountVO(cmd.getAccountName(), cmd.getDomainId(), null, cmd.getAccountType(), cmd.getRoleId(), UUID.randomUUID().toString()); + accountDao.persist((AccountVO)account); + } + + long accountId = account.getAccountId(); + clearOldAccountMapping(cmd); + LdapTrustMapVO vo = _ldapTrustMapDao.persist(new LdapTrustMapVO(cmd.getDomainId(), linkType, cmd.getLdapDomain(), cmd.getAccountType(), accountId)); + return new LinkAccountToLdapResponse(domain.getUuid(), vo.getType().toString(), vo.getName(), vo.getAccountType().ordinal(), account.getUuid(), cmd.getAccountName()); + } + + private boolean unlinkDomainFromLdap(Long domainId) { + LdapTrustMapVO vo = _ldapTrustMapDao.findByDomainId(domainId); + if (vo != null) { + removeTrustmap(vo); + return true; + } + return false; } @Override @@ -432,10 +486,6 @@ public List getDomainLinkage(long domainId){ return _ldapTrustMapDao.searchByDomainId(domainId); } - public LdapTrustMapVO getAccountLinkedToLdap(long domainId, long accountId){ - return _ldapTrustMapDao.findByAccount(domainId, accountId); - } - @Override public LdapTrustMapVO getLinkedLdapGroup(long domainId, String group) { return _ldapTrustMapDao.findGroupInDomain(domainId, group); @@ -443,41 +493,23 @@ public LdapTrustMapVO getLinkedLdapGroup(long domainId, String group) { @Override public LinkAccountToLdapResponse linkAccountToLdap(LinkAccountToLdapCmd cmd) { - Validate.notNull(_ldapConfiguration.getBaseDn(cmd.getDomainId()), "can not link an account to ldap in a domain for which no basdn is configured"); + Validate.notNull(LdapConfiguration.getBaseDn(cmd.getDomainId()), "can not link an account to ldap in a domain for which no basdn is configured"); Validate.notNull(cmd.getDomainId(), "domainId cannot be null."); Validate.notEmpty(cmd.getAccountName(), "accountName cannot be empty."); Validate.notEmpty(cmd.getLdapDomain(), "ldapDomain cannot be empty, please supply a GROUP or OU name"); Validate.notNull(cmd.getType(), "type cannot be null. It should either be GROUP or OU"); Validate.notEmpty(cmd.getLdapDomain(), "GROUP or OU name cannot be empty"); + Validate.isTrue(cmd.getAccountType() != null || cmd.getRoleId() != null, "Either account type or role ID must be given"); - LinkType linkType = LdapManager.LinkType.valueOf(cmd.getType().toUpperCase()); - Account account = accountDao.findActiveAccount(cmd.getAccountName(),cmd.getDomainId()); - if (account == null) { - account = new AccountVO(cmd.getAccountName(), cmd.getDomainId(), null, cmd.getAccountType(), UUID.randomUUID().toString()); - accountDao.persist((AccountVO)account); - } - - Long accountId = account.getAccountId(); - clearOldAccountMapping(cmd); - LdapTrustMapVO vo = _ldapTrustMapDao.persist(new LdapTrustMapVO(cmd.getDomainId(), linkType, cmd.getLdapDomain(), cmd.getAccountType(), accountId)); - DomainVO domain = domainDao.findById(vo.getDomainId()); - String domainUuid = ""; - if (domain == null) { - logger.error("no domain in database for id " + vo.getDomainId()); - } else { - domainUuid = domain.getUuid(); - } - - LinkAccountToLdapResponse response = new LinkAccountToLdapResponse(domainUuid, vo.getType().toString(), vo.getName(), vo.getAccountType().ordinal(), account.getUuid(), cmd.getAccountName()); - return response; + return linkAccountToLdapAndGetResponse(cmd); } private void clearOldAccountMapping(LinkAccountToLdapCmd cmd) { // first find if exists log warning and update LdapTrustMapVO oldVo = _ldapTrustMapDao.findGroupInDomain(cmd.getDomainId(), cmd.getLdapDomain()); - if(oldVo != null) { + if (oldVo != null) { // deal with edge cases, i.e. check if the old account is indeed deleted etc. - if (oldVo.getAccountId() != 0l) { + if (oldVo.getAccountId() != 0L) { AccountVO oldAcount = accountDao.findByIdIncludingRemoved(oldVo.getAccountId()); String msg = String.format("group %s is mapped to account %d in the current domain (%s)", cmd.getLdapDomain(), oldVo.getAccountId(), cmd.getDomainId()); if (null == oldAcount.getRemoved()) { diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapTrustMapVO.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapTrustMapVO.java index 27125998e4c5..c04c16a71ca6 100644 --- a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapTrustMapVO.java +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapTrustMapVO.java @@ -123,8 +123,8 @@ public boolean equals(Object o) { public int hashCode() { int result = type.hashCode(); result = 31 * result + name.hashCode(); - result = 31 * result + (int) (domainId ^ (domainId >>> 32)); - result = 31 * result + (int) (accountId ^ (accountId >>> 32)); + result = 31 * result + Long.hashCode(domainId); + result = 31 * result + Long.hashCode(accountId); result = 31 * result + accountType.ordinal(); return result; } diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapUser.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapUser.java index d2b58656dc58..20b13ff70a8b 100644 --- a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapUser.java +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapUser.java @@ -27,7 +27,7 @@ public class LdapUser implements Comparable { private final String username; private final String domain; private final boolean disabled; - private List memberships; + private final List memberships; public LdapUser(final String username, final String email, final String firstname, final String lastname, final String principal, String domain, boolean disabled, List memberships) { diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapUserManager.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapUserManager.java index c9fcaa23cc04..e9c7db093da9 100644 --- a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapUserManager.java +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/LdapUserManager.java @@ -26,21 +26,21 @@ public interface LdapUserManager { - public enum Provider { - MICROSOFTAD, OPENLDAP; + enum Provider { + MICROSOFTAD, OPENLDAP } - public LdapUser getUser(final String username, final LdapContext context, Long domainId) throws NamingException, IOException; + LdapUser getUser(final String username, final LdapContext context, Long domainId) throws NamingException, IOException; - public LdapUser getUser(final String username, final String type, final String name, final LdapContext context, Long domainId) throws NamingException, IOException; + LdapUser getUser(final String username, final String type, final String name, final LdapContext context, Long domainId) throws NamingException, IOException; - public List getUsers(final LdapContext context, Long domainId) throws NamingException, IOException; + List getUsers(final LdapContext context, Long domainId) throws NamingException, IOException; - public List getUsers(final String username, final LdapContext context, Long domainId) throws NamingException, IOException; + List getUsers(final String username, final LdapContext context, Long domainId) throws NamingException, IOException; - public List getUsersInGroup(String groupName, LdapContext context, Long domainId) throws NamingException; + List getUsersInGroup(String groupName, LdapContext context, Long domainId) throws NamingException; - public List searchUsers(final LdapContext context, Long domainId) throws NamingException, IOException; + List searchUsers(final LdapContext context, Long domainId) throws NamingException, IOException; - public List searchUsers(final String username, final LdapContext context, Long domainId) throws NamingException, IOException; + List searchUsers(final String username, final LdapContext context, Long domainId) throws NamingException, IOException; } diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/OpenLdapUserManagerImpl.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/OpenLdapUserManagerImpl.java index 4c125af2ea67..50ba2f34ae3b 100644 --- a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/OpenLdapUserManagerImpl.java +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/OpenLdapUserManagerImpl.java @@ -58,15 +58,15 @@ public OpenLdapUserManagerImpl(final LdapConfiguration ldapConfiguration) { protected LdapUser createUser(final SearchResult result, Long domainId) throws NamingException { final Attributes attributes = result.getAttributes(); - final String username = LdapUtils.getAttributeValue(attributes, _ldapConfiguration.getUsernameAttribute(domainId)); - final String email = LdapUtils.getAttributeValue(attributes, _ldapConfiguration.getEmailAttribute(domainId)); - final String firstname = LdapUtils.getAttributeValue(attributes, _ldapConfiguration.getFirstnameAttribute(domainId)); - final String lastname = LdapUtils.getAttributeValue(attributes, _ldapConfiguration.getLastnameAttribute(domainId)); + final String username = LdapUtils.getAttributeValue(attributes, LdapConfiguration.getUsernameAttribute(domainId)); + final String email = LdapUtils.getAttributeValue(attributes, LdapConfiguration.getEmailAttribute(domainId)); + final String firstname = LdapUtils.getAttributeValue(attributes, LdapConfiguration.getFirstnameAttribute(domainId)); + final String lastname = LdapUtils.getAttributeValue(attributes, LdapConfiguration.getLastnameAttribute(domainId)); final String principal = result.getNameInNamespace(); - final List memberships = LdapUtils.getAttributeValues(attributes, _ldapConfiguration.getUserMemberOfAttribute(domainId)); + final List memberships = LdapUtils.getAttributeValues(attributes, getMemberOfAttribute(domainId)); - String domain = principal.replace("cn=" + LdapUtils.getAttributeValue(attributes, _ldapConfiguration.getCommonNameAttribute()) + ",", ""); - domain = domain.replace("," + _ldapConfiguration.getBaseDn(domainId), ""); + String domain = principal.replace("cn=" + LdapUtils.getAttributeValue(attributes, LdapConfiguration.getCommonNameAttribute()) + ",", ""); + domain = domain.replace("," + LdapConfiguration.getBaseDn(domainId), ""); domain = domain.replace("ou=", ""); boolean disabled = isUserDisabled(result); @@ -75,23 +75,15 @@ protected LdapUser createUser(final SearchResult result, Long domainId) throws N } private String generateSearchFilter(final String username, Long domainId) { - final StringBuilder userObjectFilter = new StringBuilder(); - userObjectFilter.append("(objectClass="); - userObjectFilter.append(_ldapConfiguration.getUserObject(domainId)); - userObjectFilter.append(")"); + final StringBuilder userObjectFilter = getUserObjectFilter(domainId); - final StringBuilder usernameFilter = new StringBuilder(); - usernameFilter.append("("); - usernameFilter.append(_ldapConfiguration.getUsernameAttribute(domainId)); - usernameFilter.append("="); - usernameFilter.append((username == null ? "*" : LdapUtils.escapeLDAPSearchFilter(username))); - usernameFilter.append(")"); + final StringBuilder usernameFilter = getUsernameFilter(username, domainId); - String memberOfAttribute = _ldapConfiguration.getUserMemberOfAttribute(domainId); + String memberOfAttribute = getMemberOfAttribute(domainId); StringBuilder ldapGroupsFilter = new StringBuilder(); // this should get the trustmaps for this domain List ldapGroups = getMappedLdapGroups(domainId); - if (null != ldapGroups && ldapGroups.size() > 0) { + if (CollectionUtils.isNotEmpty(ldapGroups)) { ldapGroupsFilter.append("(|"); for (String ldapGroup : ldapGroups) { ldapGroupsFilter.append(getMemberOfGroupString(ldapGroup, memberOfAttribute)); @@ -104,21 +96,35 @@ private String generateSearchFilter(final String username, Long domainId) { if (null != pricipleGroup) { principleGroupFilter.append(getMemberOfGroupString(pricipleGroup, memberOfAttribute)); } - final StringBuilder result = new StringBuilder(); - result.append("(&"); - result.append(userObjectFilter); - result.append(usernameFilter); - result.append(ldapGroupsFilter); - result.append(principleGroupFilter); - result.append(")"); - - String returnString = result.toString(); - if (logger.isTraceEnabled()) { - logger.trace("constructed ldap query: " + returnString); - } + + String returnString = "(&" + + userObjectFilter + + usernameFilter + + ldapGroupsFilter + + principleGroupFilter + + ")"; + logger.trace("constructed ldap query: {}", returnString); return returnString; } + private StringBuilder getUsernameFilter(String username, Long domainId) { + final StringBuilder usernameFilter = new StringBuilder(); + usernameFilter.append("("); + usernameFilter.append(LdapConfiguration.getUsernameAttribute(domainId)); + usernameFilter.append("="); + usernameFilter.append((username == null ? "*" : LdapUtils.escapeLDAPSearchFilter(username))); + usernameFilter.append(")"); + return usernameFilter; + } + + StringBuilder getUserObjectFilter(Long domainId) { + final StringBuilder userObjectFilter = new StringBuilder(); + userObjectFilter.append("(objectClass="); + userObjectFilter.append(LdapConfiguration.getUserObject(domainId)); + userObjectFilter.append(")"); + return userObjectFilter; + } + private List getMappedLdapGroups(Long domainId) { List ldapGroups = new ArrayList<>(); // first get the trustmaps @@ -134,37 +140,31 @@ private List getMappedLdapGroups(Long domainId) { private String getMemberOfGroupString(String group, String memberOfAttribute) { final StringBuilder memberOfFilter = new StringBuilder(); if (null != group) { - if(logger.isDebugEnabled()) { - logger.debug("adding search filter for '" + group + - "', using '" + memberOfAttribute + "'"); - } - memberOfFilter.append("(" + memberOfAttribute + "="); - memberOfFilter.append(group); - memberOfFilter.append(")"); + logger.debug("adding search filter for '{}', using '{}'", group, memberOfAttribute); + memberOfFilter.append("(") + .append(memberOfAttribute) + .append("=") + .append(group) + .append(")"); } return memberOfFilter.toString(); } private String generateGroupSearchFilter(final String groupName, Long domainId) { - final StringBuilder groupObjectFilter = new StringBuilder(); - groupObjectFilter.append("(objectClass="); - groupObjectFilter.append(_ldapConfiguration.getGroupObject(domainId)); - groupObjectFilter.append(")"); - - final StringBuilder groupNameFilter = new StringBuilder(); - groupNameFilter.append("("); - groupNameFilter.append(_ldapConfiguration.getCommonNameAttribute()); - groupNameFilter.append("="); - groupNameFilter.append((groupName == null ? "*" : LdapUtils.escapeLDAPSearchFilter(groupName))); - groupNameFilter.append(")"); - - final StringBuilder result = new StringBuilder(); - result.append("(&"); - result.append(groupObjectFilter); - result.append(groupNameFilter); - result.append(")"); - - return result.toString(); + String groupObjectFilter = "(objectClass=" + + LdapConfiguration.getGroupObject(domainId) + + ")"; + + String groupNameFilter = "(" + + LdapConfiguration.getCommonNameAttribute() + + "=" + + (groupName == null ? "*" : LdapUtils.escapeLDAPSearchFilter(groupName)) + + ")"; + + return "(&" + + groupObjectFilter + + groupNameFilter + + ")"; } @Override @@ -183,20 +183,12 @@ public LdapUser getUser(final String username, final String type, final String n if("OU".equals(type)) { basedn = name; } else { - basedn = _ldapConfiguration.getBaseDn(domainId); + basedn = LdapConfiguration.getBaseDn(domainId); } - final StringBuilder userObjectFilter = new StringBuilder(); - userObjectFilter.append("(objectClass="); - userObjectFilter.append(_ldapConfiguration.getUserObject(domainId)); - userObjectFilter.append(")"); + final StringBuilder userObjectFilter = getUserObjectFilter(domainId); - final StringBuilder usernameFilter = new StringBuilder(); - usernameFilter.append("("); - usernameFilter.append(_ldapConfiguration.getUsernameAttribute(domainId)); - usernameFilter.append("="); - usernameFilter.append((username == null ? "*" : LdapUtils.escapeLDAPSearchFilter(username))); - usernameFilter.append(")"); + final StringBuilder usernameFilter = getUsernameFilter(username, domainId); final StringBuilder memberOfFilter = new StringBuilder(); if ("GROUP".equals(type)) { @@ -205,18 +197,17 @@ public LdapUser getUser(final String username, final String type, final String n memberOfFilter.append(")"); } - final StringBuilder searchQuery = new StringBuilder(); - searchQuery.append("(&"); - searchQuery.append(userObjectFilter); - searchQuery.append(usernameFilter); - searchQuery.append(memberOfFilter); - searchQuery.append(")"); + String searchQuery = "(&" + + userObjectFilter + + usernameFilter + + memberOfFilter + + ")"; - return searchUser(basedn, searchQuery.toString(), context, domainId); + return searchUser(basedn, searchQuery, context, domainId); } protected String getMemberOfAttribute(final Long domainId) { - return _ldapConfiguration.getUserMemberOfAttribute(domainId); + return LdapConfiguration.getUserMemberOfAttribute(domainId); } @Override @@ -236,14 +227,14 @@ public List getUsers(final String username, final LdapContext context, @Override public List getUsersInGroup(String groupName, LdapContext context, Long domainId) throws NamingException { - String attributeName = _ldapConfiguration.getGroupUniqueMemberAttribute(domainId); + String attributeName = LdapConfiguration.getGroupUniqueMemberAttribute(domainId); final SearchControls controls = new SearchControls(); controls.setSearchScope(_ldapConfiguration.getScope()); controls.setReturningAttributes(new String[] {attributeName}); - NamingEnumeration result = context.search(_ldapConfiguration.getBaseDn(domainId), generateGroupSearchFilter(groupName, domainId), controls); + NamingEnumeration result = context.search(LdapConfiguration.getBaseDn(domainId), generateGroupSearchFilter(groupName, domainId), controls); - final List users = new ArrayList(); + final List users = new ArrayList<>(); //Expecting only one result which has all the users if (result.hasMoreElements()) { Attribute attribute = result.nextElement().getAttributes().get(attributeName); @@ -254,7 +245,7 @@ public List getUsersInGroup(String groupName, LdapContext context, Lon try{ users.add(getUserForDn(userdn, context, domainId)); } catch (NamingException e){ - logger.info("Userdn: " + userdn + " Not Found:: Exception message: " + e.getMessage()); + logger.info("Userdn: {} Not Found:: Exception message: {}", userdn, e.getMessage()); } } } @@ -269,7 +260,7 @@ private LdapUser getUserForDn(String userdn, LdapContext context, Long domainId) controls.setSearchScope(_ldapConfiguration.getScope()); controls.setReturningAttributes(_ldapConfiguration.getReturnAttributes(domainId)); - NamingEnumeration result = context.search(userdn, "(objectClass=" + _ldapConfiguration.getUserObject(domainId) + ")", controls); + NamingEnumeration result = context.search(userdn, "(objectClass=" + LdapConfiguration.getUserObject(domainId) + ")", controls); if (result.hasMoreElements()) { return createUser(result.nextElement(), domainId); } else { @@ -286,17 +277,15 @@ protected boolean isUserDisabled(SearchResult result) throws NamingException { return false; } - public LdapUser searchUser(final String basedn, final String searchString, final LdapContext context, Long domainId) throws NamingException, IOException { + public LdapUser searchUser(final String basedn, final String searchString, final LdapContext context, Long domainId) throws NamingException { final SearchControls searchControls = new SearchControls(); searchControls.setSearchScope(_ldapConfiguration.getScope()); searchControls.setReturningAttributes(_ldapConfiguration.getReturnAttributes(domainId)); NamingEnumeration results = context.search(basedn, searchString, searchControls); - if(logger.isDebugEnabled()) { - logger.debug("searching user(s) with filter: \"" + searchString + "\""); - } - final List users = new ArrayList(); + logger.debug("searching user(s) with filter: \"{}\"", searchString); + final List users = new ArrayList<>(); while (results.hasMoreElements()) { final SearchResult result = results.nextElement(); users.add(createUser(result, domainId)); @@ -317,14 +306,14 @@ public List searchUsers(final String username, final LdapContext conte searchControls.setSearchScope(_ldapConfiguration.getScope()); searchControls.setReturningAttributes(_ldapConfiguration.getReturnAttributes(domainId)); - String basedn = _ldapConfiguration.getBaseDn(domainId); + String basedn = LdapConfiguration.getBaseDn(domainId); if (StringUtils.isBlank(basedn)) { throw new IllegalArgumentException(String.format("ldap basedn is not configured (for domain: %s)", domainId)); } byte[] cookie = null; - int pageSize = _ldapConfiguration.getLdapPageSize(domainId); + int pageSize = LdapConfiguration.getLdapPageSize(domainId); context.setRequestControls(new Control[]{new PagedResultsControl(pageSize, Control.NONCRITICAL)}); - final List users = new ArrayList(); + final List users = new ArrayList<>(); NamingEnumeration results; do { results = context.search(basedn, generateSearchFilter(username, domainId), searchControls); diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/dao/LdapConfigurationDao.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/dao/LdapConfigurationDao.java index af774b685ed6..e757def83cef 100644 --- a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/dao/LdapConfigurationDao.java +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/dao/LdapConfigurationDao.java @@ -29,17 +29,15 @@ public interface LdapConfigurationDao extends GenericDao { /** * @deprecated there might well be more then one ldap implementation on a host and or a double binding of several domains - * @param hostname - * @return + * @param hostname the hostname or ip address of the LDAP server + * @return the LDAP configuration for the given hostname */ @Deprecated LdapConfigurationVO findByHostname(String hostname); LdapConfigurationVO find(String hostname, int port, Long domainId); - LdapConfigurationVO find(String hostname, int port, Long domainId, boolean listAll); - Pair, Integer> searchConfigurations(String hostname, int port, Long domainId); - Pair, Integer> searchConfigurations(String hostname, int port, Long domainId, boolean listAll); + Pair, Integer> searchConfigurations(Long id, String hostname, int port, Long domainId, boolean listAll); } diff --git a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/dao/LdapConfigurationDaoImpl.java b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/dao/LdapConfigurationDaoImpl.java index c053e87b6bf6..1ce52b9fd0d9 100644 --- a/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/dao/LdapConfigurationDaoImpl.java +++ b/plugins/user-authenticators/ldap/src/main/java/org/apache/cloudstack/ldap/dao/LdapConfigurationDaoImpl.java @@ -48,6 +48,7 @@ public LdapConfigurationDaoImpl() { listGlobalConfigurationsSearch.done(); listDomainConfigurationsSearch = createSearchBuilder(); + listDomainConfigurationsSearch.and("id", listDomainConfigurationsSearch.entity().getId(), SearchCriteria.Op.EQ); listDomainConfigurationsSearch.and("hostname", listDomainConfigurationsSearch.entity().getHostname(), Op.EQ); listDomainConfigurationsSearch.and("port", listDomainConfigurationsSearch.entity().getPort(), Op.EQ); listDomainConfigurationsSearch.and("domain_id", listDomainConfigurationsSearch.entity().getDomainId(), Op.EQ); @@ -63,31 +64,29 @@ public LdapConfigurationVO findByHostname(final String hostname) { @Override public LdapConfigurationVO find(String hostname, int port, Long domainId) { - SearchCriteria sc = getSearchCriteria(hostname, port, domainId, false); - return findOneBy(sc); - } - - @Override - public LdapConfigurationVO find(String hostname, int port, Long domainId, boolean listAll) { - SearchCriteria sc = getSearchCriteria(hostname, port, domainId, listAll); + SearchCriteria sc = getSearchCriteria(null, hostname, port, domainId, false); return findOneBy(sc); } @Override public Pair, Integer> searchConfigurations(final String hostname, final int port, final Long domainId) { - SearchCriteria sc = getSearchCriteria(hostname, port, domainId, false); + SearchCriteria sc = getSearchCriteria(null, hostname, port, domainId, false); return searchAndCount(sc, null); } @Override - public Pair, Integer> searchConfigurations(final String hostname, final int port, final Long domainId, final boolean listAll) { - SearchCriteria sc = getSearchCriteria(hostname, port, domainId, listAll); + public Pair, Integer> searchConfigurations(final Long id, final String hostname, final int port, final Long domainId, final boolean listAll) { + SearchCriteria sc = getSearchCriteria(id, hostname, port, domainId, listAll); return searchAndCount(sc, null); } - private SearchCriteria getSearchCriteria(String hostname, int port, Long domainId,boolean listAll) { + private SearchCriteria getSearchCriteria(Long id, String hostname, int port, Long domainId,boolean listAll) { SearchCriteria sc; - if (domainId != null) { + if (id != null) { + // If id is present, ignore all other parameters + sc = listDomainConfigurationsSearch.create(); + sc.setParameters("id", id); + } else if (domainId != null) { // If domainid is present, ignore listall sc = listDomainConfigurationsSearch.create(); sc.setParameters("domain_id", domainId); diff --git a/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LdapListUsersCmdTest.java b/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LdapListUsersCmdTest.java index bd55926e8e00..6af40c5df403 100644 --- a/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LdapListUsersCmdTest.java +++ b/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LdapListUsersCmdTest.java @@ -167,7 +167,7 @@ public void isACloudstackUser() { LdapUser ldapUser = new LdapUser("rmurphy", "rmurphy@cloudstack.org", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null, false, null); - boolean result = ldapListUsersCmd.isACloudstackUser(ldapUser); + boolean result = ldapListUsersCmd.isACloudStackUser(ldapUser); assertTrue(result); } @@ -183,7 +183,7 @@ public void isNotACloudstackUser() { LdapUser ldapUser = new LdapUser("rmurphy", "rmurphy@cloudstack.org", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null, false, null); - boolean result = ldapListUsersCmd.isACloudstackUser(ldapUser); + boolean result = ldapListUsersCmd.isACloudStackUser(ldapUser); assertFalse(result); } diff --git a/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LinkAccountToLdapCmdTest.java b/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LinkAccountToLdapCmdTest.java index 62a3a809b16f..e616bd573789 100644 --- a/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LinkAccountToLdapCmdTest.java +++ b/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LinkAccountToLdapCmdTest.java @@ -19,6 +19,7 @@ import com.cloud.user.Account; import com.cloud.user.AccountService; +import com.cloud.user.DomainService; import com.cloud.user.User; import com.cloud.user.UserAccountVO; import org.apache.cloudstack.acl.RoleType; @@ -45,6 +46,8 @@ public class LinkAccountToLdapCmdTest implements LdapConfigurationChanger { LdapManager ldapManager; @Mock AccountService accountService; + @Mock + DomainService domainService; LinkAccountToLdapCmd linkAccountToLdapCmd; @@ -53,6 +56,7 @@ public void setUp() throws NoSuchFieldException, IllegalAccessException { linkAccountToLdapCmd = new LinkAccountToLdapCmd(); setHiddenField(linkAccountToLdapCmd, "_ldapManager", ldapManager); setHiddenField(linkAccountToLdapCmd, "_accountService", accountService); + setHiddenField(linkAccountToLdapCmd, "_domainService", domainService); } @Test @@ -94,5 +98,6 @@ public void execute() throws Exception { assertEquals("type", type, result.getType()); assertEquals("name", ldapDomain, result.getLdapDomain()); assertEquals("accountId", String.valueOf(accountId), result.getAdminId()); + assertEquals("accountName", String.valueOf(accountName), result.getAccountName()); } } diff --git a/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LinkDomainToLdapCmdTest.java b/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LinkDomainToLdapCmdTest.java index 67d0e7705226..080347fefd32 100644 --- a/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LinkDomainToLdapCmdTest.java +++ b/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/api/command/LinkDomainToLdapCmdTest.java @@ -18,6 +18,7 @@ import com.cloud.user.Account; import com.cloud.user.AccountService; +import com.cloud.user.DomainService; import com.cloud.user.User; import com.cloud.user.UserAccountVO; import org.apache.cloudstack.acl.RoleType; @@ -44,6 +45,8 @@ public class LinkDomainToLdapCmdTest implements LdapConfigurationChanger LdapManager ldapManager; @Mock AccountService accountService; + @Mock + DomainService domainService; LinkDomainToLdapCmd linkDomainToLdapCmd; @@ -52,6 +55,7 @@ public void setUp() throws NoSuchFieldException, IllegalAccessException { linkDomainToLdapCmd = new LinkDomainToLdapCmd(); setHiddenField(linkDomainToLdapCmd, "_ldapManager", ldapManager); setHiddenField(linkDomainToLdapCmd, "_accountService", accountService); + setHiddenField(linkDomainToLdapCmd, "_domainService", domainService); } @After diff --git a/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/ldap/ADLdapUserManagerImplTest.java b/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/ldap/ADLdapUserManagerImplTest.java index 58b14ec3684e..6e9944599e56 100644 --- a/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/ldap/ADLdapUserManagerImplTest.java +++ b/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/ldap/ADLdapUserManagerImplTest.java @@ -16,17 +16,21 @@ // under the License. package org.apache.cloudstack.ldap; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; import javax.naming.directory.SearchControls; import javax.naming.ldap.LdapContext; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.when; @@ -35,41 +39,46 @@ public class ADLdapUserManagerImplTest { ADLdapUserManagerImpl adLdapUserManager; + MockedStatic LdapConfiguration; @Mock - LdapConfiguration ldapConfiguration; + LdapConfiguration ldapConfigurationMock; @Before - public void init() throws Exception { + public void init() { + LdapConfiguration = Mockito.mockStatic(LdapConfiguration.class,Mockito.CALLS_REAL_METHODS); adLdapUserManager = new ADLdapUserManagerImpl(); - adLdapUserManager._ldapConfiguration = ldapConfiguration; + adLdapUserManager._ldapConfiguration = ldapConfigurationMock; } + @After + public void afterEach() { + LdapConfiguration.close(); + } @Test public void testGenerateADSearchFilterWithNestedGroupsEnabled() { - when(ldapConfiguration.getUserObject(any())).thenReturn("user"); - when(ldapConfiguration.getCommonNameAttribute()).thenReturn("CN"); - when(ldapConfiguration.getBaseDn(any())).thenReturn("DC=cloud,DC=citrix,DC=com"); - when(ldapConfiguration.isNestedGroupsEnabled(any())).thenReturn(true); + when(adLdapUserManager._ldapConfiguration.getUserObject(anyLong())).thenReturn("user"); + when(adLdapUserManager._ldapConfiguration.getCommonNameAttribute()).thenReturn("CN"); + when(adLdapUserManager._ldapConfiguration.getBaseDn(any())).thenReturn("DC=cloud,DC=citrix,DC=com"); + when(adLdapUserManager._ldapConfiguration.isNestedGroupsEnabled(anyLong())).thenReturn(true); String [] groups = {"dev", "dev-hyd"}; for (String group: groups) { String result = adLdapUserManager.generateADGroupSearchFilter(group, 1L); - assertTrue(("(&(objectClass=user)(memberOf:1.2.840.113556.1.4.1941:=CN=" + group + ",DC=cloud,DC=citrix,DC=com))").equals(result)); + assertEquals(("(&(&(objectCategory=person)(objectClass=user))(memberOf:1.2.840.113556.1.4.1941:=CN=" + group + ",DC=cloud,DC=citrix,DC=com))"), result); } - } @Test public void testGenerateADSearchFilterWithNestedGroupsDisabled() { - when(ldapConfiguration.getUserObject(any())).thenReturn("user"); - when(ldapConfiguration.getCommonNameAttribute()).thenReturn("CN"); - when(ldapConfiguration.getBaseDn(any())).thenReturn("DC=cloud,DC=citrix,DC=com"); - when(ldapConfiguration.isNestedGroupsEnabled(any())).thenReturn(false); + when(adLdapUserManager._ldapConfiguration.getUserObject(anyLong())).thenReturn("user"); + when(adLdapUserManager._ldapConfiguration.getCommonNameAttribute()).thenReturn("CN"); + when(adLdapUserManager._ldapConfiguration.getBaseDn(anyLong())).thenReturn("DC=cloud,DC=citrix,DC=com"); + when(adLdapUserManager._ldapConfiguration.isNestedGroupsEnabled(anyLong())).thenReturn(false); String [] groups = {"dev", "dev-hyd"}; for (String group: groups) { String result = adLdapUserManager.generateADGroupSearchFilter(group, 1L); - assertTrue(("(&(objectClass=user)(memberOf=CN=" + group + ",DC=cloud,DC=citrix,DC=com))").equals(result)); + assertEquals(("(&(&(objectCategory=person)(objectClass=user))(memberOf=CN=" + group + ",DC=cloud,DC=citrix,DC=com))"), result); } } @@ -79,9 +88,9 @@ public void testGenerateADSearchFilterWithNestedGroupsDisabled() { @Test(expected = IllegalArgumentException.class) public void testGetUsersInGroupUsingNullGroup() throws Exception { String[] returnAttributes = {"username", "firstname", "lastname", "email"}; - lenient().when(ldapConfiguration.getScope()).thenReturn(SearchControls.SUBTREE_SCOPE); - lenient().when(ldapConfiguration.getReturnAttributes(null)).thenReturn(returnAttributes); - lenient().when(ldapConfiguration.getBaseDn(any())).thenReturn(null).thenReturn(null).thenReturn("DC=cloud,DC=citrix,DC=com"); + lenient().when(adLdapUserManager._ldapConfiguration.getScope()).thenReturn(SearchControls.SUBTREE_SCOPE); + lenient().when(adLdapUserManager._ldapConfiguration.getReturnAttributes(null)).thenReturn(returnAttributes); + lenient().when(adLdapUserManager._ldapConfiguration.getBaseDn(any())).thenReturn(null).thenReturn(null).thenReturn("DC=cloud,DC=citrix,DC=com"); LdapContext context = ldapContext; String [] groups = {null, "group", null}; diff --git a/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/ldap/LdapDirectoryServerConnectionTest.java b/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/ldap/LdapDirectoryServerConnectionTest.java index 2d2690f8e3a4..a216084aa3a0 100644 --- a/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/ldap/LdapDirectoryServerConnectionTest.java +++ b/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/ldap/LdapDirectoryServerConnectionTest.java @@ -88,7 +88,7 @@ public void setup() throws Exception { ldapTestConfigTool.overrideConfigValue(configuration, "ldapBindPrincipal", "uid=admin,ou=system"); ldapTestConfigTool.overrideConfigValue(configuration, "ldapMemberOfAttribute", "memberOf"); lenient().when(userManagerFactory.getInstance(LdapUserManager.Provider.OPENLDAP)).thenReturn(new OpenLdapUserManagerImpl(configuration)); - // construct an ellaborate structure around a single object + // construct an elaborate structure around a single object Pair, Integer> vos = new Pair, Integer>( Collections.singletonList(configurationVO),1); lenient().when(configurationDao.searchConfigurations(null, 0, 1L)).thenReturn(vos); @@ -149,7 +149,7 @@ public void testEmbeddedLdapAvailable() { public void testSchemaLoading() { try { assertTrue("standard not loaded", embeddedLdapServer.addSchemaFromClasspath("other")); -// we need member of in ACS nowadays (backwards comptability broken): +// we need member of in ACS nowadays (backwards compatibility broken): // assertTrue("memberOf schema not loaded", embeddedLdapServer.addSchemaFromPath(new File("src/test/resources/memberOf"), "microsoft")); } catch (LdapException | IOException e) { fail(e.getLocalizedMessage()); diff --git a/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/ldap/LdapTestConfigTool.java b/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/ldap/LdapTestConfigTool.java index 47f201de4799..4197bad4f2d9 100644 --- a/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/ldap/LdapTestConfigTool.java +++ b/plugins/user-authenticators/ldap/src/test/java/org/apache/cloudstack/ldap/LdapTestConfigTool.java @@ -19,7 +19,6 @@ import org.apache.cloudstack.framework.config.ConfigKey; import java.lang.reflect.Field; -import java.lang.reflect.Modifier; public class LdapTestConfigTool { public LdapTestConfigTool() { @@ -31,18 +30,12 @@ void overrideConfigValue(LdapConfiguration ldapConfiguration, final String confi ConfigKey key = (ConfigKey)configKey.get(ldapConfiguration); - Field modifiersField = Field.class.getDeclaredField("modifiers"); - modifiersField.setAccessible(true); - modifiersField.setInt(configKey, configKey.getModifiers() & ~Modifier.FINAL); - Field f = ConfigKey.class.getDeclaredField("_value"); f.setAccessible(true); - modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL); f.set(key, o); Field dynamic = ConfigKey.class.getDeclaredField("_isDynamic"); dynamic.setAccessible(true); - modifiersField.setInt(dynamic, dynamic.getModifiers() & ~Modifier.FINAL); dynamic.setBoolean(key, false); } } diff --git a/plugins/user-authenticators/ldap/src/test/resources/log4j.xml b/plugins/user-authenticators/ldap/src/test/resources/log4j.xml index c369c454640e..6c0ffa264e19 100755 --- a/plugins/user-authenticators/ldap/src/test/resources/log4j.xml +++ b/plugins/user-authenticators/ldap/src/test/resources/log4j.xml @@ -32,7 +32,7 @@ - + diff --git a/plugins/user-authenticators/md5/pom.xml b/plugins/user-authenticators/md5/pom.xml index e63f97743418..f0f998c42c16 100644 --- a/plugins/user-authenticators/md5/pom.xml +++ b/plugins/user-authenticators/md5/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.0.0-SNAPSHOT + 4.23.0.0-SNAPSHOT ../../pom.xml diff --git a/plugins/user-authenticators/md5/src/main/resources/META-INF/cloudstack/md5/spring-md5-context.xml b/plugins/user-authenticators/md5/src/main/resources/META-INF/cloudstack/md5/spring-md5-context.xml index 132f1481bb69..a447e7317437 100644 --- a/plugins/user-authenticators/md5/src/main/resources/META-INF/cloudstack/md5/spring-md5-context.xml +++ b/plugins/user-authenticators/md5/src/main/resources/META-INF/cloudstack/md5/spring-md5-context.xml @@ -30,5 +30,5 @@ - + diff --git a/plugins/user-authenticators/oauth2/pom.xml b/plugins/user-authenticators/oauth2/pom.xml index 5a1e49874a89..6ab7b9f5faba 100644 --- a/plugins/user-authenticators/oauth2/pom.xml +++ b/plugins/user-authenticators/oauth2/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.0.0-SNAPSHOT + 4.23.0.0-SNAPSHOT ../../pom.xml diff --git a/plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/OAuth2AuthManagerImpl.java b/plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/OAuth2AuthManagerImpl.java index 6d7123ebe8e7..b65027d6a249 100644 --- a/plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/OAuth2AuthManagerImpl.java +++ b/plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/OAuth2AuthManagerImpl.java @@ -136,9 +136,9 @@ public String verifyCodeAndFetchEmail(String code, String provider) { public OauthProviderVO registerOauthProvider(RegisterOAuthProviderCmd cmd) { String description = cmd.getDescription(); String provider = cmd.getProvider(); - String clientId = cmd.getClientId(); - String redirectUri = cmd.getRedirectUri(); - String secretKey = cmd.getSecretKey(); + String clientId = StringUtils.trim(cmd.getClientId()); + String redirectUri = StringUtils.trim(cmd.getRedirectUri()); + String secretKey = StringUtils.trim(cmd.getSecretKey()); if (!isOAuthPluginEnabled()) { throw new CloudRuntimeException("OAuth is not enabled, please enable to register"); @@ -168,9 +168,9 @@ public List listOauthProviders(String provider, String uuid) { public OauthProviderVO updateOauthProvider(UpdateOAuthProviderCmd cmd) { Long id = cmd.getId(); String description = cmd.getDescription(); - String clientId = cmd.getClientId(); - String redirectUri = cmd.getRedirectUri(); - String secretKey = cmd.getSecretKey(); + String clientId = StringUtils.trim(cmd.getClientId()); + String redirectUri = StringUtils.trim(cmd.getRedirectUri()); + String secretKey = StringUtils.trim(cmd.getSecretKey()); Boolean enabled = cmd.getEnabled(); OauthProviderVO providerVO = _oauthProviderDao.findById(id); diff --git a/plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/OAuth2UserAuthenticator.java b/plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/OAuth2UserAuthenticator.java index 1f38adfd63bc..dde50c8bb34d 100644 --- a/plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/OAuth2UserAuthenticator.java +++ b/plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/OAuth2UserAuthenticator.java @@ -31,15 +31,17 @@ import javax.inject.Inject; import java.util.Map; +import static org.apache.cloudstack.oauth2.OAuth2AuthManager.OAuth2IsPluginEnabled; + public class OAuth2UserAuthenticator extends AdapterBase implements UserAuthenticator { @Inject - private UserAccountDao _userAccountDao; + private UserAccountDao userAccountDao; @Inject - private UserDao _userDao; + private UserDao userDao; @Inject - private OAuth2AuthManager _userOAuth2mgr; + private OAuth2AuthManager userOAuth2mgr; @Override public Pair authenticate(String username, String password, Long domainId, Map requestParameters) { @@ -47,20 +49,33 @@ public Pair authenticate(String username, logger.debug("Trying OAuth2 auth for user: " + username); } - final UserAccount userAccount = _userAccountDao.getUserAccount(username, domainId); + if (!isOAuthPluginEnabled()) { + logger.debug("OAuth2 plugin is disabled"); + return new Pair(false, null); + } else if (requestParameters == null) { + logger.debug("Request parameters are null"); + return new Pair(false, null); + } + + final UserAccount userAccount = userAccountDao.getUserAccount(username, domainId); if (userAccount == null) { logger.debug("Unable to find user with " + username + " in domain " + domainId + ", or user source is not OAUTH2"); return new Pair(false, null); } else { - User user = _userDao.getUser(userAccount.getId()); + User user = userDao.getUser(userAccount.getId()); final String[] provider = (String[])requestParameters.get(ApiConstants.PROVIDER); final String[] emailArray = (String[])requestParameters.get(ApiConstants.EMAIL); final String[] secretCodeArray = (String[])requestParameters.get(ApiConstants.SECRET_CODE); + + if (provider == null) { + return new Pair(false, null); + } + String oauthProvider = ((provider == null) ? null : provider[0]); String email = ((emailArray == null) ? null : emailArray[0]); String secretCode = ((secretCodeArray == null) ? null : secretCodeArray[0]); - UserOAuth2Authenticator authenticator = _userOAuth2mgr.getUserOAuth2AuthenticationProvider(oauthProvider); + UserOAuth2Authenticator authenticator = userOAuth2mgr.getUserOAuth2AuthenticationProvider(oauthProvider); if (user != null && authenticator.verifyUser(email, secretCode)) { return new Pair(true, null); } @@ -73,4 +88,8 @@ public Pair authenticate(String username, public String encode(String password) { return null; } + + protected boolean isOAuthPluginEnabled() { + return OAuth2IsPluginEnabled.value(); + } } diff --git a/plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/api/command/OauthLoginAPIAuthenticatorCmd.java b/plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/api/command/OauthLoginAPIAuthenticatorCmd.java index f9a1d10d3526..d2af4c24ce43 100644 --- a/plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/api/command/OauthLoginAPIAuthenticatorCmd.java +++ b/plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/api/command/OauthLoginAPIAuthenticatorCmd.java @@ -34,6 +34,8 @@ import org.apache.cloudstack.api.auth.APIAuthenticator; import org.apache.cloudstack.api.auth.PluggableAPIAuthenticator; import org.apache.cloudstack.api.response.LoginCmdResponse; +import org.apache.cloudstack.resourcedetail.UserDetailVO; +import org.apache.cloudstack.resourcedetail.dao.UserDetailsDao; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.Nullable; @@ -74,6 +76,9 @@ public class OauthLoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent @Inject ApiServerService _apiServer; + @Inject + UserDetailsDao userDetailsDao; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -157,8 +162,10 @@ private String doOauthAuthentication(HttpSession session, Long domainId, String if (userAccount != null && User.Source.SAML2 == userAccount.getSource()) { throw new CloudAuthenticationException("User is not allowed CloudStack login"); } - return ApiResponseSerializer.toSerializedString(_apiServer.loginUser(session, userAccount.getUsername(), null, domainId, domain, remoteAddress, params), + serializedResponse = ApiResponseSerializer.toSerializedString(_apiServer.loginUser(session, userAccount.getUsername(), null, domainId, domain, remoteAddress, params), responseType); + userDetailsDao.addDetail(userAccount.getId(), UserDetailVO.OauthLogin, "true", false); + return serializedResponse; } catch (final CloudAuthenticationException ex) { ApiServlet.invalidateHttpSession(session, "fall through to API key,"); String msg = String.format("%s", ex.getMessage() != null ? @@ -176,18 +183,14 @@ private String doOauthAuthentication(HttpSession session, Long domainId, String } protected Long getDomainIdFromParams(Map params, StringBuilder auditTrailSb, String responseType) { - String[] domainIdArr = (String[])params.get(ApiConstants.DOMAIN_ID); - - if (domainIdArr == null) { - domainIdArr = (String[])params.get(ApiConstants.DOMAIN__ID); - } + String domainIdStr = _apiServer.getDomainId(params); Long domainId = null; - if ((domainIdArr != null) && (domainIdArr.length > 0)) { + if (StringUtils.isNotEmpty(domainIdStr)) { try { //check if UUID is passed in for domain - domainId = _apiServer.fetchDomainId(domainIdArr[0]); + domainId = _apiServer.fetchDomainId(domainIdStr); if (domainId == null) { - domainId = Long.parseLong(domainIdArr[0]); + domainId = Long.parseLong(domainIdStr); } auditTrailSb.append(" domainid=" + domainId);// building the params for POST call } catch (final NumberFormatException e) { diff --git a/plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/api/command/VerifyOAuthCodeAndGetUserCmd.java b/plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/api/command/VerifyOAuthCodeAndGetUserCmd.java index bd49f87d6273..b3d2d335ba25 100644 --- a/plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/api/command/VerifyOAuthCodeAndGetUserCmd.java +++ b/plugins/user-authenticators/oauth2/src/main/java/org/apache/cloudstack/oauth2/api/command/VerifyOAuthCodeAndGetUserCmd.java @@ -20,8 +20,10 @@ import java.util.List; import java.util.Map; -import com.cloud.api.response.ApiResponseSerializer; -import com.cloud.user.Account; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -37,13 +39,13 @@ import org.apache.cloudstack.oauth2.api.response.OauthProviderResponse; import org.apache.commons.lang.ArrayUtils; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; +import com.cloud.api.response.ApiResponseSerializer; +import com.cloud.user.Account; @APICommand(name = "verifyOAuthCodeAndGetUser", description = "Verify the OAuth Code and fetch the corresponding user from provider", responseObject = OauthProviderResponse.class, entityType = {}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, - authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}, since = "4.19.0") + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}, since = "4.19.0", + httpMethod = "GET") public class VerifyOAuthCodeAndGetUserCmd extends BaseListCmd implements APIAuthenticator { ///////////////////////////////////////////////////// diff --git a/plugins/user-authenticators/oauth2/src/test/java/org/apache/cloudstack/oauth2/OAuth2UserAuthenticatorTest.java b/plugins/user-authenticators/oauth2/src/test/java/org/apache/cloudstack/oauth2/OAuth2UserAuthenticatorTest.java index c0d273a5fa5a..d1c1889ba999 100644 --- a/plugins/user-authenticators/oauth2/src/test/java/org/apache/cloudstack/oauth2/OAuth2UserAuthenticatorTest.java +++ b/plugins/user-authenticators/oauth2/src/test/java/org/apache/cloudstack/oauth2/OAuth2UserAuthenticatorTest.java @@ -27,21 +27,29 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; 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.assertNull; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.doReturn; +@RunWith(MockitoJUnitRunner.class) public class OAuth2UserAuthenticatorTest { @Mock @@ -54,13 +62,14 @@ public class OAuth2UserAuthenticatorTest { private OAuth2AuthManager userOAuth2mgr; @InjectMocks + @Spy private OAuth2UserAuthenticator authenticator; - private AutoCloseable closeable; @Before public void setUp() { closeable = MockitoAnnotations.openMocks(this); + doReturn(true).when(authenticator).isOAuthPluginEnabled(); } @After @@ -68,6 +77,7 @@ public void tearDown() throws Exception { closeable.close(); } + @Test public void testAuthenticateWithValidCredentials() { String username = "testuser"; @@ -92,13 +102,13 @@ public void testAuthenticateWithValidCredentials() { Pair result = authenticator.authenticate(username, null, domainId, requestParameters); + assertTrue(result.first()); + assertNull(result.second()); + verify(userAccountDao).getUserAccount(username, domainId); verify(userDao).getUser(userAccount.getId()); verify(userOAuth2mgr).getUserOAuth2AuthenticationProvider(provider[0]); verify(userOAuth2Authenticator).verifyUser(email[0], secretCode[0]); - - assertEquals(true, result.first().booleanValue()); - assertEquals(null, result.second()); } @Test @@ -114,7 +124,7 @@ public void testAuthenticateWithInvalidCredentials() { UserOAuth2Authenticator userOAuth2Authenticator = mock(UserOAuth2Authenticator.class); when(userAccountDao.getUserAccount(username, domainId)).thenReturn(userAccount); - when(userDao.getUser(userAccount.getId())).thenReturn( user); + when(userDao.getUser(userAccount.getId())).thenReturn(user); when(userOAuth2mgr.getUserOAuth2AuthenticationProvider(provider[0])).thenReturn(userOAuth2Authenticator); when(userOAuth2Authenticator.verifyUser(email[0], secretCode[0])).thenReturn(false); @@ -125,13 +135,13 @@ public void testAuthenticateWithInvalidCredentials() { Pair result = authenticator.authenticate(username, null, domainId, requestParameters); + assertFalse(result.first()); + assertEquals(OAuth2UserAuthenticator.ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT, result.second()); + verify(userAccountDao).getUserAccount(username, domainId); verify(userDao).getUser(userAccount.getId()); verify(userOAuth2mgr).getUserOAuth2AuthenticationProvider(provider[0]); verify(userOAuth2Authenticator).verifyUser(email[0], secretCode[0]); - - assertEquals(false, result.first().booleanValue()); - assertEquals(OAuth2UserAuthenticator.ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT, result.second()); } @Test @@ -151,11 +161,11 @@ public void testAuthenticateWithInvalidUserAccount() { Pair result = authenticator.authenticate(username, null, domainId, requestParameters); + assertFalse(result.first()); + assertNull(result.second()); + verify(userAccountDao).getUserAccount(username, domainId); verify(userDao, never()).getUser(anyLong()); verify(userOAuth2mgr, never()).getUserOAuth2AuthenticationProvider(anyString()); - - assertEquals(false, result.first().booleanValue()); - assertEquals(null, result.second()); } } diff --git a/plugins/user-authenticators/oauth2/src/test/java/org/apache/cloudstack/oauth2/api/command/OauthLoginAPIAuthenticatorCmdTest.java b/plugins/user-authenticators/oauth2/src/test/java/org/apache/cloudstack/oauth2/api/command/OauthLoginAPIAuthenticatorCmdTest.java index ccbb53cfc80d..962ffefd5ce3 100644 --- a/plugins/user-authenticators/oauth2/src/test/java/org/apache/cloudstack/oauth2/api/command/OauthLoginAPIAuthenticatorCmdTest.java +++ b/plugins/user-authenticators/oauth2/src/test/java/org/apache/cloudstack/oauth2/api/command/OauthLoginAPIAuthenticatorCmdTest.java @@ -85,10 +85,29 @@ public void testGetDomainIdFromParams() { ApiServer apiServer = mock(ApiServer.class); cmd._apiServer = apiServer; when(apiServer.fetchDomainId("1234")).thenReturn(5678L); + when(apiServer.getDomainId(params)).thenCallRealMethod(); Long domainId = cmd.getDomainIdFromParams(params, auditTrailSb, responseType); assertEquals(Long.valueOf(5678), domainId); assertEquals(" domainid=5678", auditTrailSb.toString()); } + + @Test + public void testGetDomainIdFromCamelCaseParam() { + StringBuilder auditTrailSb = new StringBuilder(); + String responseType = "json"; + Map params = new HashMap<>(); + params.put(ApiConstants.DOMAIN_ID, null); + params.put(ApiConstants.DOMAIN__ID, new String[]{"5678"}); + ApiServer apiServer = mock(ApiServer.class); + cmd._apiServer = apiServer; + when(apiServer.fetchDomainId("5678")).thenReturn(1234L); + when(apiServer.getDomainId(params)).thenCallRealMethod(); + + Long domainId = cmd.getDomainIdFromParams(params, auditTrailSb, responseType); + + assertEquals(Long.valueOf(1234), domainId); + assertEquals(" domainid=1234", auditTrailSb.toString()); + } } diff --git a/plugins/user-authenticators/pbkdf2/pom.xml b/plugins/user-authenticators/pbkdf2/pom.xml index f030e38a6a44..6ffa1188e13c 100644 --- a/plugins/user-authenticators/pbkdf2/pom.xml +++ b/plugins/user-authenticators/pbkdf2/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.0.0-SNAPSHOT + 4.23.0.0-SNAPSHOT ../../pom.xml diff --git a/plugins/user-authenticators/plain-text/pom.xml b/plugins/user-authenticators/plain-text/pom.xml index e378ec8399b1..54a9650b64ff 100644 --- a/plugins/user-authenticators/plain-text/pom.xml +++ b/plugins/user-authenticators/plain-text/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.0.0-SNAPSHOT + 4.23.0.0-SNAPSHOT ../../pom.xml diff --git a/plugins/user-authenticators/plain-text/src/main/resources/META-INF/cloudstack/plaintext/spring-plaintext-context.xml b/plugins/user-authenticators/plain-text/src/main/resources/META-INF/cloudstack/plaintext/spring-plaintext-context.xml index fccff88c4cb6..0b22b010e191 100644 --- a/plugins/user-authenticators/plain-text/src/main/resources/META-INF/cloudstack/plaintext/spring-plaintext-context.xml +++ b/plugins/user-authenticators/plain-text/src/main/resources/META-INF/cloudstack/plaintext/spring-plaintext-context.xml @@ -31,5 +31,5 @@ - + diff --git a/plugins/user-authenticators/saml2/pom.xml b/plugins/user-authenticators/saml2/pom.xml index 7a19768fab74..4cd6f70d096c 100644 --- a/plugins/user-authenticators/saml2/pom.xml +++ b/plugins/user-authenticators/saml2/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.0.0-SNAPSHOT + 4.23.0.0-SNAPSHOT ../../pom.xml @@ -59,6 +59,12 @@ assertj-core ${cs.assertj.version} test + + + net.bytebuddy + byte-buddy + + diff --git a/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/command/AuthorizeSAMLSSOCmd.java b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/command/AuthorizeSAMLSSOCmd.java index c5f48d61c6fb..48cee26a775d 100644 --- a/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/command/AuthorizeSAMLSSOCmd.java +++ b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/command/AuthorizeSAMLSSOCmd.java @@ -83,7 +83,7 @@ public void execute() { _accountService.checkAccess(CallContext.current().getCallingAccount(), domain); _accountService.checkAccess(CallContext.current().getCallingAccount(), SecurityChecker.AccessType.OperateEntry, true, account); - CallContext.current().setEventDetails("UserId: " + getId()); + CallContext.current().setEventDetails("User ID: " + getResourceUuid(ApiConstants.USER_ID)); SuccessResponse response = new SuccessResponse(); Boolean status = false; diff --git a/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/command/ListAndSwitchSAMLAccountCmd.java b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/command/ListAndSwitchSAMLAccountCmd.java index 3e6b093abe13..5263ade6b37a 100644 --- a/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/command/ListAndSwitchSAMLAccountCmd.java +++ b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/command/ListAndSwitchSAMLAccountCmd.java @@ -47,6 +47,7 @@ import org.apache.cloudstack.saml.SAML2AuthManager; import org.apache.cloudstack.saml.SAMLUtils; +import com.cloud.api.ApiServer; import com.cloud.api.response.ApiResponseSerializer; import com.cloud.domain.Domain; import com.cloud.domain.dao.DomainDao; @@ -59,6 +60,8 @@ import com.cloud.user.dao.UserDao; import com.cloud.utils.HttpUtils; +import org.apache.commons.lang3.EnumUtils; + @APICommand(name = "listAndSwitchSamlAccount", description = "Lists and switches to other SAML accounts owned by the SAML user", responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListAndSwitchSAMLAccountCmd extends BaseCmd implements APIAuthenticator { @@ -78,10 +81,10 @@ public class ListAndSwitchSAMLAccountCmd extends BaseCmd implements APIAuthentic //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.USER_ID, type = CommandType.UUID, entityType = UserResponse.class, required = false, description = "User uuid") + @Parameter(name = ApiConstants.USER_ID, type = CommandType.UUID, entityType = UserResponse.class, description = "User uuid") private Long userId; - @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, required = false, description = "Domain uuid") + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "Domain uuid") private Long domainId; @Override @@ -102,7 +105,9 @@ public String authenticate(final String command, final Map par params, responseType)); } - if (!HttpUtils.validateSessionKey(session, params, req.getCookies(), ApiConstants.SESSIONKEY)) { + HttpUtils.ApiSessionKeyCheckOption sessionKeyCheckOption = EnumUtils.getEnumIgnoreCase(HttpUtils.ApiSessionKeyCheckOption.class, + ApiServer.ApiSessionKeyCheckLocations.value(), HttpUtils.ApiSessionKeyCheckOption.CookieAndParameter); + if (!HttpUtils.validateSessionKey(session, params, req.getCookies(), ApiConstants.SESSIONKEY, sessionKeyCheckOption)) { throw new ServerApiException(ApiErrorCode.UNAUTHORIZED, _apiServer.getSerializedApiError(ApiErrorCode.UNAUTHORIZED.getHttpCode(), "Unauthorized session, please re-login", params, responseType)); @@ -126,10 +131,12 @@ public String authenticate(final String command, final Map par } if (userUuid != null && domainUuid != null) { + logger.debug("User [" + currentUserAccount.getUsername() + "] is requesting to switch from user profile [" + currentUserAccount.getId() + "] to useraccount [" + userUuid + "] in domain [" + domainUuid + "]"); final User user = _userDao.findByUuid(userUuid); final Domain domain = _domainDao.findByUuid(domainUuid); final UserAccount nextUserAccount = _accountService.getUserAccountById(user.getId()); if (nextUserAccount != null && !nextUserAccount.getAccountState().equals(Account.State.ENABLED.toString())) { + logger.warn("User [" + currentUserAccount.getUsername() + "] is requesting to switch from user profile [" + currentUserId + "] to user profile [" + userUuid + "] in domain [" + domainUuid + "] but the associated target account [" + nextUserAccount.getAccountName() + "] is not enabled"); throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.PARAM_ERROR.getHttpCode(), "The requested user account is locked and cannot be switched to, please contact your administrator.", params, responseType)); @@ -140,25 +147,31 @@ public String authenticate(final String command, final Map par || !nextUserAccount.getExternalEntity().equals(currentUserAccount.getExternalEntity()) || (nextUserAccount.getDomainId() != domain.getId()) || (nextUserAccount.getSource() != User.Source.SAML2)) { + logger.warn("User [" + currentUserAccount.getUsername() + "] is requesting to switch from user profile [" + currentUserId + "] to user profile [" + userUuid + "] in domain [" + domainUuid + "] but the associated target account is not found or invalid"); throw new ServerApiException(ApiErrorCode.PARAM_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.PARAM_ERROR.getHttpCode(), "User account is not allowed to switch to the requested account", params, responseType)); } try { if (_apiServer.verifyUser(nextUserAccount.getId())) { + logger.info("User [" + currentUserAccount.getUsername() + "] user profile switch is accepted: from [" + currentUserId + "] to user profile [" + userUuid + "] in domain [" + domainUuid + "] with account [" + nextUserAccount.getAccountName() + "]"); + // need to set a sessoin variable to inform the login function of the specific user to login as, rather than using email only (which could have multiple matches) + session.setAttribute("nextUserId", user.getId()); final LoginCmdResponse loginResponse = (LoginCmdResponse) _apiServer.loginUser(session, nextUserAccount.getUsername(), nextUserAccount.getUsername() + nextUserAccount.getSource().toString(), nextUserAccount.getDomainId(), null, remoteAddress, params); SAMLUtils.setupSamlUserCookies(loginResponse, resp); - resp.sendRedirect(SAML2AuthManager.SAMLCloudStackRedirectionUrl.value()); + session.removeAttribute("nextUserId"); + logger.debug("User [" + currentUserAccount.getUsername() + "] user profile switch cookies set: from [" + currentUserId + "] to user profile [" + userUuid + "] in domain [" + domainUuid + "] with account [" + nextUserAccount.getAccountName() + "]"); + //resp.sendRedirect(SAML2AuthManager.SAMLCloudStackRedirectionUrl.value()); return ApiResponseSerializer.toSerializedString(loginResponse, responseType); } } catch (CloudAuthenticationException | IOException exception) { - logger.debug("Failed to switch to request SAML user account due to: " + exception.getMessage()); + logger.debug("User [{}] user profile switch cookies set FAILED: from [{}] to user profile [{}] in domain [{}] with account [{}]", currentUserAccount.getUsername(), currentUserId, userUuid, domainUuid, nextUserAccount.getAccountName(), exception); } } else { List switchableAccounts = _userAccountDao.getAllUsersByNameAndEntity(currentUserAccount.getUsername(), currentUserAccount.getExternalEntity()); - if (switchableAccounts != null && switchableAccounts.size() > 0 && currentUserId != User.UID_SYSTEM) { - List accountResponses = new ArrayList(); + if (switchableAccounts != null && !switchableAccounts.isEmpty() && currentUserId != User.UID_SYSTEM) { + List accountResponses = new ArrayList<>(); for (UserAccountVO userAccount: switchableAccounts) { User user = _userDao.getUser(userAccount.getId()); Domain domain = _domainService.getDomain(userAccount.getDomainId()); @@ -171,8 +184,9 @@ public String authenticate(final String command, final Map par accountResponse.setAccountName(userAccount.getAccountName()); accountResponse.setIdpId(user.getExternalEntity()); accountResponses.add(accountResponse); + logger.debug("Returning available useraccount for [{}]: UserUUID: [{}], DomainUUID: [{}], Account: [{}]", currentUserAccount.getUsername(), user.getUuid(), domain.getUuid(), userAccount.getAccountName()); } - ListResponse response = new ListResponse(); + ListResponse response = new ListResponse<>(); response.setResponses(accountResponses); response.setResponseName(getCommandName()); return ApiResponseSerializer.toSerializedString(response, responseType); @@ -191,7 +205,7 @@ public APIAuthenticationType getAPIType() { @Override public void setAuthenticators(List authenticators) { for (PluggableAPIAuthenticator authManager: authenticators) { - if (authManager != null && authManager instanceof SAML2AuthManager) { + if (authManager instanceof SAML2AuthManager) { _samlAuthManager = (SAML2AuthManager) authManager; } } diff --git a/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmd.java b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmd.java index fb4f4cc00a52..584f24637540 100644 --- a/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmd.java +++ b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmd.java @@ -78,7 +78,7 @@ import com.cloud.user.dao.UserAccountDao; import com.cloud.utils.db.EntityManager; -@APICommand(name = "samlSso", description = "SP initiated SAML Single Sign On", requestHasSensitiveInfo = true, responseObject = LoginCmdResponse.class, entityType = {}) +@APICommand(name = "samlSso", description = "SP initiated SAML Single Sign On", responseObject = LoginCmdResponse.class) public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthenticator, Configurable { private static final String s_name = "loginresponse"; @@ -97,7 +97,7 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent @Inject private UserAccountDao userAccountDao; - protected static ConfigKey saml2FailedLoginRedirectUrl = new ConfigKey("Advanced", String.class, "saml2.failed.login.redirect.url", "", + protected static ConfigKey saml2FailedLoginRedirectUrl = new ConfigKey<>("Advanced", String.class, "saml2.failed.login.redirect.url", "", "The URL to redirect the SAML2 login failed message (the default vaulue is empty).", true); SAML2AuthManager samlAuthManager; @@ -142,6 +142,14 @@ public Response processSAMLResponse(String responseMessage) { return responseObject; } + protected void checkAndFailOnMissingSAMLSignature(Signature signature) { + if (signature == null && SAML2AuthManager.SAMLCheckSignature.value()) { + logger.error("Failing SAML login due to missing signature in the SAML response and signature check is enforced. " + + "Please check and ensure the IDP configuration has signing certificate or relax the saml2.check.signature setting."); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Signature is missing from the SAML Response. Please contact the Administrator"); + } + } + @Override public String authenticate(final String command, final Map params, final HttpSession session, final InetAddress remoteAddress, final String responseType, final StringBuilder auditTrailSb, final HttpServletRequest req, final HttpServletResponse resp) throws ServerApiException { try { @@ -150,11 +158,17 @@ public String authenticate(final String command, final Map par String domainPath = null; if (params.containsKey(ApiConstants.IDP_ID)) { - idpId = ((String[])params.get(ApiConstants.IDP_ID))[0]; + String[] idpIds = (String[])params.get(ApiConstants.IDP_ID); + if (idpIds != null && idpIds.length > 0) { + idpId = idpIds[0]; + } } if (params.containsKey(ApiConstants.DOMAIN)) { - domainPath = ((String[])params.get(ApiConstants.DOMAIN))[0]; + String[] domainPaths = (String[])params.get(ApiConstants.DOMAIN); + if (domainPaths != null && domainPaths.length > 0) { + domainPath = domainPaths[0]; + } } if (domainPath != null && !domainPath.isEmpty()) { @@ -182,7 +196,7 @@ public String authenticate(final String command, final Map par String authnId = SAMLUtils.generateSecureRandomId(); samlAuthManager.saveToken(authnId, domainPath, idpMetadata.getEntityId()); logger.debug("Sending SAMLRequest id=" + authnId); - String redirectUrl = SAMLUtils.buildAuthnRequestUrl(authnId, spMetadata, idpMetadata, SAML2AuthManager.SAMLSignatureAlgorithm.value()); + String redirectUrl = SAMLUtils.buildAuthnRequestUrl(authnId, spMetadata, idpMetadata, SAML2AuthManager.SAMLSignatureAlgorithm.value(), SAML2AuthManager.SAMLRequirePasswordLogin.value()); resp.sendRedirect(redirectUrl); return ""; } if (params.containsKey("SAMLart")) { @@ -199,7 +213,7 @@ public String authenticate(final String command, final Map par params, responseType)); } - String username = null; + String username; Issuer issuer = processedSAMLResponse.getIssuer(); SAMLProviderMetadata spMetadata = samlAuthManager.getSPMetadata(); SAMLProviderMetadata idpMetadata = samlAuthManager.getIdPMetadata(issuer.getValue()); @@ -218,11 +232,13 @@ public String authenticate(final String command, final Map par "Received SAML response for a SSO request that we may not have made or has expired, please try logging in again", params, responseType)); } + samlAuthManager.purgeToken(token); // Set IdpId for this session session.setAttribute(SAMLPluginConstants.SAML_IDPID, issuer.getValue()); Signature sig = processedSAMLResponse.getSignature(); + checkAndFailOnMissingSAMLSignature(sig); if (idpMetadata.getSigningCertificate() != null && sig != null) { BasicX509Credential credential = new BasicX509Credential(); credential.setEntityCertificate(idpMetadata.getSigningCertificate()); @@ -236,9 +252,8 @@ public String authenticate(final String command, final Map par params, responseType)); } } - if (username == null) { - username = SAMLUtils.getValueFromAssertions(processedSAMLResponse.getAssertions(), SAML2AuthManager.SAMLUserAttributeName.value()); - } + + username = SAMLUtils.getValueFromAssertions(processedSAMLResponse.getAssertions(), SAML2AuthManager.SAMLUserAttributeName.value()); for (Assertion assertion: processedSAMLResponse.getAssertions()) { if (assertion!= null && assertion.getSubject() != null && assertion.getSubject().getNameID() != null) { @@ -264,12 +279,13 @@ public String authenticate(final String command, final Map par try { assertion = decrypter.decrypt(encryptedAssertion); } catch (DecryptionException e) { - logger.warn("SAML EncryptedAssertion error: " + e.toString()); + logger.warn("SAML EncryptedAssertion error: " + e); } if (assertion == null) { continue; } Signature encSig = assertion.getSignature(); + checkAndFailOnMissingSAMLSignature(encSig); if (idpMetadata.getSigningCertificate() != null && encSig != null) { BasicX509Credential sigCredential = new BasicX509Credential(); sigCredential.setEntityCertificate(idpMetadata.getSigningCertificate()); @@ -300,7 +316,7 @@ public String authenticate(final String command, final Map par UserAccount userAccount = null; List possibleUserAccounts = userAccountDao.getAllUsersByNameAndEntity(username, issuer.getValue()); - if (possibleUserAccounts != null && possibleUserAccounts.size() > 0) { + if (possibleUserAccounts != null && !possibleUserAccounts.isEmpty()) { // Log into the first enabled user account // Users can switch to other allowed accounts later for (UserAccountVO possibleUserAccount : possibleUserAccounts) { @@ -360,7 +376,7 @@ public APIAuthenticationType getAPIType() { @Override public void setAuthenticators(List authenticators) { for (PluggableAPIAuthenticator authManager: authenticators) { - if (authManager != null && authManager instanceof SAML2AuthManager) { + if (authManager instanceof SAML2AuthManager) { samlAuthManager = (SAML2AuthManager) authManager; } } diff --git a/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/response/SamlAuthorizationResponse.java b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/response/SamlAuthorizationResponse.java index 445ee887af7e..4937efe9dfce 100644 --- a/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/response/SamlAuthorizationResponse.java +++ b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/response/SamlAuthorizationResponse.java @@ -25,15 +25,15 @@ @EntityReference(value = User.class) public class SamlAuthorizationResponse extends BaseResponse { @SerializedName("userid") - @Param(description = "the user ID") + @Param(description = "The user ID") private String userId; @SerializedName("status") - @Param(description = "the SAML authorization status") + @Param(description = "The SAML authorization status") private Boolean status; @SerializedName("idpid") - @Param(description = "the authorized Identity Provider ID") + @Param(description = "The authorized Identity Provider ID") private String idpId; public SamlAuthorizationResponse(String userId, Boolean status, String idpId) { diff --git a/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAML2AuthManager.java b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAML2AuthManager.java index e52a7e32695f..618536a71f66 100644 --- a/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAML2AuthManager.java +++ b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAML2AuthManager.java @@ -25,59 +25,76 @@ public interface SAML2AuthManager extends PluggableAPIAuthenticator, PluggableService { - public static final ConfigKey SAMLIsPluginEnabled = new ConfigKey("Advanced", Boolean.class, "saml2.enabled", "false", + ConfigKey SAMLIsPluginEnabled = new ConfigKey("Advanced", Boolean.class, "saml2.enabled", "false", "Indicates whether SAML SSO plugin is enabled or not", true); - public static final ConfigKey SAMLServiceProviderID = new ConfigKey("Advanced", String.class, "saml2.sp.id", "org.apache.cloudstack", + ConfigKey SAMLServiceProviderID = new ConfigKey("Advanced", String.class, "saml2.sp.id", "org.apache.cloudstack", "SAML2 Service Provider Identifier String", true); - public static final ConfigKey SAMLServiceProviderContactPersonName = new ConfigKey("Advanced", String.class, "saml2.sp.contact.person", "CloudStack Developers", + ConfigKey SAMLServiceProviderContactPersonName = new ConfigKey("Advanced", String.class, "saml2.sp.contact.person", "CloudStack Developers", "SAML2 Service Provider Contact Person Name", true); - public static final ConfigKey SAMLServiceProviderContactEmail = new ConfigKey("Advanced", String.class, "saml2.sp.contact.email", "dev@cloudstack.apache.org", + ConfigKey SAMLServiceProviderContactEmail = new ConfigKey("Advanced", String.class, "saml2.sp.contact.email", "dev@cloudstack.apache.org", "SAML2 Service Provider Contact Email Address", true); - public static final ConfigKey SAMLServiceProviderOrgName = new ConfigKey("Advanced", String.class, "saml2.sp.org.name", "Apache CloudStack", + ConfigKey SAMLServiceProviderOrgName = new ConfigKey("Advanced", String.class, "saml2.sp.org.name", "Apache CloudStack", "SAML2 Service Provider Organization Name", true); - public static final ConfigKey SAMLServiceProviderOrgUrl = new ConfigKey("Advanced", String.class, "saml2.sp.org.url", "http://cloudstack.apache.org", + ConfigKey SAMLServiceProviderOrgUrl = new ConfigKey("Advanced", String.class, "saml2.sp.org.url", "http://cloudstack.apache.org", "SAML2 Service Provider Organization URL", true); - public static final ConfigKey SAMLServiceProviderSingleSignOnURL = new ConfigKey("Advanced", String.class, "saml2.sp.sso.url", "http://localhost:8080/client/api?command=samlSso", + ConfigKey SAMLServiceProviderSingleSignOnURL = new ConfigKey("Advanced", String.class, "saml2.sp.sso.url", "http://localhost:8080/client/api?command=samlSso", "SAML2 CloudStack Service Provider Single Sign On URL", true); - public static final ConfigKey SAMLServiceProviderSingleLogOutURL = new ConfigKey("Advanced", String.class, "saml2.sp.slo.url", "http://localhost:8080/client/", + ConfigKey SAMLServiceProviderSingleLogOutURL = new ConfigKey("Advanced", String.class, "saml2.sp.slo.url", "http://localhost:8080/client/", "SAML2 CloudStack Service Provider Single Log Out URL", true); - public static final ConfigKey SAMLCloudStackRedirectionUrl = new ConfigKey("Advanced", String.class, "saml2.redirect.url", "http://localhost:8080/client", + ConfigKey SAMLCloudStackRedirectionUrl = new ConfigKey("Advanced", String.class, "saml2.redirect.url", "http://localhost:8080/client", "The CloudStack UI url the SSO should redirected to when successful", true); - public static final ConfigKey SAMLUserAttributeName = new ConfigKey("Advanced", String.class, "saml2.user.attribute", "uid", + ConfigKey SAMLUserAttributeName = new ConfigKey("Advanced", String.class, "saml2.user.attribute", "uid", "Attribute name to be looked for in SAML response that will contain the username", true); - public static final ConfigKey SAMLIdentityProviderMetadataURL = new ConfigKey("Advanced", String.class, "saml2.idp.metadata.url", "https://openidp.feide.no/simplesaml/saml2/idp/metadata.php", + ConfigKey SAMLIdentityProviderMetadataURL = new ConfigKey("Advanced", String.class, "saml2.idp.metadata.url", "https://openidp.feide.no/simplesaml/saml2/idp/metadata.php", "SAML2 Identity Provider Metadata XML Url", true); - public static final ConfigKey SAMLDefaultIdentityProviderId = new ConfigKey("Advanced", String.class, "saml2.default.idpid", "https://openidp.feide.no", + ConfigKey SAMLDefaultIdentityProviderId = new ConfigKey("Advanced", String.class, "saml2.default.idpid", "https://openidp.feide.no", "The default IdP entity ID to use only in case of multiple IdPs", true); - public static final ConfigKey SAMLSignatureAlgorithm = new ConfigKey<>(String.class, "saml2.sigalg", "Advanced", "SHA1", + ConfigKey SAMLSignatureAlgorithm = new ConfigKey<>(String.class, "saml2.sigalg", "Advanced", "SHA1", "The algorithm to use to when signing a SAML request. Default is SHA1, allowed algorithms: SHA1, SHA256, SHA384, SHA512", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.Select, "SHA1,SHA256,SHA384,SHA512"); - public static final ConfigKey SAMLAppendDomainSuffix = new ConfigKey("Advanced", Boolean.class, "saml2.append.idpdomain", "false", + ConfigKey SAMLAppendDomainSuffix = new ConfigKey("Advanced", Boolean.class, "saml2.append.idpdomain", "false", "If enabled, create account/user dialog with SAML SSO enabled will append the IdP domain to the user or account name in the UI dialog", true); - public static final ConfigKey SAMLTimeout = new ConfigKey("Advanced", Integer.class, "saml2.timeout", "1800", + ConfigKey SAMLTimeout = new ConfigKey("Advanced", Integer.class, "saml2.timeout", "1800", "SAML2 IDP Metadata refresh interval in seconds, minimum value is set to 300", true); - public SAMLProviderMetadata getSPMetadata(); - public SAMLProviderMetadata getIdPMetadata(String entityId); - public Collection getAllIdPMetadata(); + ConfigKey SAMLCheckSignature = new ConfigKey("Advanced", Boolean.class, "saml2.check.signature", "true", + "When enabled (default and recommended), SAML2 signature checks are enforced and lack of signature in the SAML SSO response will cause login exception. Disabling this is not advisable but provided for backward compatibility for users who are able to accept the risks.", false); - public boolean isUserAuthorized(Long userId, String entityId); - public boolean authorizeUser(Long userId, String entityId, boolean enable); + ConfigKey SAMLForceAuthn = new ConfigKey("Advanced", Boolean.class, "saml2.force.authn", "false", + "When enabled (default false), SAML2 will force a new authentication. This can be useful if multiple application use different saml logins from the same application (I.E. browser)", true); - public void saveToken(String authnId, String domain, String entity); - public SAMLTokenVO getToken(String authnId); - public void expireTokens(); + ConfigKey SAMLUserSessionKeyPathAttribute = new ConfigKey("Advanced", String.class, "saml2.user.sessionkey.path", "", + "The Path attribute of sessionkey cookie when SAML users have logged in. If not set, it will be set to the path of SAML redirection URL (saml2.redirect.url).", true); + + ConfigKey SAMLRequirePasswordLogin = new ConfigKey("Advanced", Boolean.class, "saml2.require.password", "true", + "When enabled SAML2 will validate that the SAML login was performed with a password. If disabled, other forms of authentication are allowed (two-factor, certificate, etc) on the SAML Authentication Provider", true); + + ConfigKey EnableLoginAfterSAMLDisable = new ConfigKey<>("Advanced", Boolean.class, "enable.login.with.disabled.saml", "false", "When enabled, if SAML SSO is disabled, enables user to login with user and password, otherwise a user with SAML SSO disabled cannot login", true); + + + + SAMLProviderMetadata getSPMetadata(); + SAMLProviderMetadata getIdPMetadata(String entityId); + Collection getAllIdPMetadata(); + + boolean isUserAuthorized(Long userId, String entityId); + boolean authorizeUser(Long userId, String entityId, boolean enable); + + void saveToken(String authnId, String domain, String entity); + SAMLTokenVO getToken(String authnId); + void purgeToken(SAMLTokenVO token); + void expireTokens(); } diff --git a/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAML2AuthManagerImpl.java b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAML2AuthManagerImpl.java index 0e8790d65586..9f8101b867d4 100644 --- a/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAML2AuthManagerImpl.java +++ b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAML2AuthManagerImpl.java @@ -451,8 +451,13 @@ public boolean authorizeUser(Long userId, String entityId, boolean enable) { user.setExternalEntity(entityId); user.setSource(User.Source.SAML2); } else { + boolean enableLoginAfterSAMLDisable = SAML2AuthManager.EnableLoginAfterSAMLDisable.value(); if (user.getSource().equals(User.Source.SAML2)) { - user.setSource(User.Source.SAML2DISABLED); + if(enableLoginAfterSAMLDisable) { + user.setSource(User.Source.UNKNOWN); + } else { + user.setSource(User.Source.SAML2DISABLED); + } } else { return false; } @@ -485,6 +490,13 @@ public SAMLTokenVO getToken(String authnId) { return _samlTokenDao.findByUuid(authnId); } + @Override + public void purgeToken(SAMLTokenVO token) { + if (token != null) { + _samlTokenDao.remove(token.getId()); + } + } + @Override public void expireTokens() { _samlTokenDao.expireTokens(); @@ -533,6 +545,7 @@ public ConfigKey[] getConfigKeys() { SAMLServiceProviderSingleSignOnURL, SAMLServiceProviderSingleLogOutURL, SAMLCloudStackRedirectionUrl, SAMLUserAttributeName, SAMLIdentityProviderMetadataURL, SAMLDefaultIdentityProviderId, - SAMLSignatureAlgorithm, SAMLAppendDomainSuffix, SAMLTimeout}; + SAMLSignatureAlgorithm, SAMLAppendDomainSuffix, SAMLTimeout, SAMLCheckSignature, + SAMLForceAuthn, SAMLUserSessionKeyPathAttribute, SAMLRequirePasswordLogin, EnableLoginAfterSAMLDisable}; } } diff --git a/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAMLUtils.java b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAMLUtils.java index 7ffe07a8609f..1a9d677d43a5 100644 --- a/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAMLUtils.java +++ b/plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAMLUtils.java @@ -25,6 +25,8 @@ import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.math.BigInteger; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URLEncoder; import java.nio.charset.Charset; import java.security.InvalidKeyException; @@ -102,7 +104,9 @@ import org.w3c.dom.Element; import org.xml.sax.SAXException; +import com.cloud.api.ApiServlet; import com.cloud.utils.HttpUtils; +import com.cloud.utils.exception.CloudRuntimeException; public class SAMLUtils { protected static Logger LOGGER = LogManager.getLogger(SAMLUtils.class); @@ -148,11 +152,11 @@ public static String getValueFromAssertions(final List assertions, fi return null; } - public static String buildAuthnRequestUrl(final String authnId, final SAMLProviderMetadata spMetadata, final SAMLProviderMetadata idpMetadata, final String signatureAlgorithm) { + public static String buildAuthnRequestUrl(final String authnId, final SAMLProviderMetadata spMetadata, final SAMLProviderMetadata idpMetadata, final String signatureAlgorithm, boolean requirePasswordAuthentication) { String redirectUrl = ""; try { DefaultBootstrap.bootstrap(); - AuthnRequest authnRequest = SAMLUtils.buildAuthnRequestObject(authnId, spMetadata.getEntityId(), idpMetadata.getSsoUrl(), spMetadata.getSsoUrl()); + AuthnRequest authnRequest = SAMLUtils.buildAuthnRequestObject(authnId, spMetadata.getEntityId(), idpMetadata.getSsoUrl(), spMetadata.getSsoUrl(), requirePasswordAuthentication); PrivateKey privateKey = null; if (spMetadata.getKeyPair() != null) { privateKey = spMetadata.getKeyPair().getPrivate(); @@ -165,43 +169,50 @@ public static String buildAuthnRequestUrl(final String authnId, final SAMLProvid return redirectUrl; } - public static AuthnRequest buildAuthnRequestObject(final String authnId, final String spId, final String idpUrl, final String consumerUrl) { + public static AuthnRequest buildAuthnRequestObject(final String authnId, final String spId, final String idpUrl, final String consumerUrl, boolean requirePasswordAuthentication) { // Issuer object IssuerBuilder issuerBuilder = new IssuerBuilder(); Issuer issuer = issuerBuilder.buildObject(); issuer.setValue(spId); - // AuthnContextClass - AuthnContextClassRefBuilder authnContextClassRefBuilder = new AuthnContextClassRefBuilder(); - AuthnContextClassRef authnContextClassRef = authnContextClassRefBuilder.buildObject( - SAMLConstants.SAML20_NS, - "AuthnContextClassRef", "saml"); - authnContextClassRef.setAuthnContextClassRef(AuthnContext.PPT_AUTHN_CTX); - - // AuthnContext - RequestedAuthnContextBuilder requestedAuthnContextBuilder = new RequestedAuthnContextBuilder(); - RequestedAuthnContext requestedAuthnContext = requestedAuthnContextBuilder.buildObject(); - requestedAuthnContext.setComparison(AuthnContextComparisonTypeEnumeration.EXACT); - requestedAuthnContext.getAuthnContextClassRefs().add(authnContextClassRef); - // Creation of AuthRequestObject AuthnRequestBuilder authRequestBuilder = new AuthnRequestBuilder(); AuthnRequest authnRequest = authRequestBuilder.buildObject(); + + // AuthnContextClass. When this is false, the authentication requirements are defered to the SAML IDP and its default or configured workflow + if (requirePasswordAuthentication) { + setRequestedAuthnContext(authnRequest, requirePasswordAuthentication); + } + authnRequest.setID(authnId); authnRequest.setDestination(idpUrl); authnRequest.setVersion(SAMLVersion.VERSION_20); - authnRequest.setForceAuthn(false); + authnRequest.setForceAuthn(SAML2AuthManager.SAMLForceAuthn.value()); authnRequest.setIsPassive(false); authnRequest.setIssueInstant(new DateTime()); authnRequest.setProtocolBinding(SAMLConstants.SAML2_POST_BINDING_URI); authnRequest.setAssertionConsumerServiceURL(consumerUrl); authnRequest.setProviderName(spId); authnRequest.setIssuer(issuer); - authnRequest.setRequestedAuthnContext(requestedAuthnContext); return authnRequest; } + public static void setRequestedAuthnContext(AuthnRequest authnRequest, boolean requirePasswordAuthentication) { + AuthnContextClassRefBuilder authnContextClassRefBuilder = new AuthnContextClassRefBuilder(); + AuthnContextClassRef authnContextClassRef = authnContextClassRefBuilder.buildObject( + SAMLConstants.SAML20_NS, + "AuthnContextClassRef", "saml"); + authnContextClassRef.setAuthnContextClassRef(AuthnContext.PPT_AUTHN_CTX); + + // AuthnContext + RequestedAuthnContextBuilder requestedAuthnContextBuilder = new RequestedAuthnContextBuilder(); + RequestedAuthnContext requestedAuthnContext = requestedAuthnContextBuilder.buildObject(); + requestedAuthnContext.setComparison(AuthnContextComparisonTypeEnumeration.EXACT); + requestedAuthnContext.getAuthnContextClassRefs().add(authnContextClassRef); + authnRequest.setRequestedAuthnContext(requestedAuthnContext); + } + public static LogoutRequest buildLogoutRequest(String logoutUrl, String spId, String nameIdString) { Issuer issuer = new IssuerBuilder().buildObject(); issuer.setValue(spId); @@ -281,23 +292,56 @@ public static String generateSAMLRequestSignature(final String urlEncodedString, } public static void setupSamlUserCookies(final LoginCmdResponse loginResponse, final HttpServletResponse resp) throws IOException { - resp.addCookie(new Cookie("userid", URLEncoder.encode(loginResponse.getUserId(), HttpUtils.UTF_8))); - resp.addCookie(new Cookie("domainid", URLEncoder.encode(loginResponse.getDomainId(), HttpUtils.UTF_8))); - resp.addCookie(new Cookie("role", URLEncoder.encode(loginResponse.getType(), HttpUtils.UTF_8))); - resp.addCookie(new Cookie("username", URLEncoder.encode(loginResponse.getUsername(), HttpUtils.UTF_8))); - resp.addCookie(new Cookie("account", URLEncoder.encode(loginResponse.getAccount(), HttpUtils.UTF_8))); - resp.addCookie(new Cookie("isSAML", URLEncoder.encode("true", HttpUtils.UTF_8))); - resp.addCookie(new Cookie("twoFaEnabled", URLEncoder.encode(loginResponse.is2FAenabled(), HttpUtils.UTF_8))); + String redirectUrl = SAML2AuthManager.SAMLCloudStackRedirectionUrl.value(); + String path = SAML2AuthManager.SAMLUserSessionKeyPathAttribute.value(); + String domain = null; + try { + URI redirectUri = new URI(redirectUrl); + domain = redirectUri.getHost(); + if (StringUtils.isBlank(path)) { + path = redirectUri.getPath(); + } + if (StringUtils.isBlank(path)) { + path = "/"; + } + } catch (URISyntaxException ex) { + throw new CloudRuntimeException("Invalid URI: " + redirectUrl); + } + + addBaseCookies(loginResponse, resp, domain, path); + String providerFor2FA = loginResponse.getProviderFor2FA(); if (StringUtils.isNotEmpty(providerFor2FA)) { - resp.addCookie(new Cookie("twoFaProvider", URLEncoder.encode(loginResponse.getProviderFor2FA(), HttpUtils.UTF_8))); + resp.addCookie(newCookie(domain, path,"twoFaProvider", URLEncoder.encode(loginResponse.getProviderFor2FA(), HttpUtils.UTF_8))); } String timezone = loginResponse.getTimeZone(); if (timezone != null) { - resp.addCookie(new Cookie("timezone", URLEncoder.encode(timezone, HttpUtils.UTF_8))); + resp.addCookie(newCookie(domain, path,"timezone", URLEncoder.encode(timezone, HttpUtils.UTF_8))); } - resp.addCookie(new Cookie("userfullname", URLEncoder.encode(loginResponse.getFirstName() + " " + loginResponse.getLastName(), HttpUtils.UTF_8).replace("+", "%20"))); - resp.addHeader("SET-COOKIE", String.format("%s=%s;HttpOnly;Path=/client/api", ApiConstants.SESSIONKEY, loginResponse.getSessionKey())); + + String sameSite = ApiServlet.getApiSessionKeySameSite(); + String sessionKeyCookie = String.format("%s=%s;Domain=%s;Path=%s;%s", ApiConstants.SESSIONKEY, loginResponse.getSessionKey(), domain, path, sameSite); + LOGGER.debug("Adding sessionkey cookie to response: " + sessionKeyCookie); + resp.addHeader("SET-COOKIE", sessionKeyCookie); + resp.addHeader("SET-COOKIE", String.format("%s=%s;HttpOnly;Path=/client/api;%s", ApiConstants.SESSIONKEY, loginResponse.getSessionKey(), sameSite)); + } + + private static void addBaseCookies(final LoginCmdResponse loginResponse, final HttpServletResponse resp, String domain, String path) throws IOException { + resp.addCookie(newCookie(domain, path, "userid", URLEncoder.encode(loginResponse.getUserId(), HttpUtils.UTF_8))); + resp.addCookie(newCookie(domain, path,"domainid", URLEncoder.encode(loginResponse.getDomainId(), HttpUtils.UTF_8))); + resp.addCookie(newCookie(domain, path,"role", URLEncoder.encode(loginResponse.getType(), HttpUtils.UTF_8))); + resp.addCookie(newCookie(domain, path,"username", URLEncoder.encode(loginResponse.getUsername(), HttpUtils.UTF_8))); + resp.addCookie(newCookie(domain, path,"account", URLEncoder.encode(loginResponse.getAccount(), HttpUtils.UTF_8))); + resp.addCookie(newCookie(domain, path,"isSAML", URLEncoder.encode("true", HttpUtils.UTF_8))); + resp.addCookie(newCookie(domain, path,"twoFaEnabled", URLEncoder.encode(loginResponse.is2FAenabled(), HttpUtils.UTF_8))); + resp.addCookie(newCookie(domain, path,"userfullname", URLEncoder.encode(loginResponse.getFirstName() + " " + loginResponse.getLastName(), HttpUtils.UTF_8).replace("+", "%20"))); + } + + private static Cookie newCookie(final String domain, final String path, final String name, final String value) { + Cookie cookie = new Cookie(name, value); + cookie.setDomain(domain); + cookie.setPath(path); + return cookie; } /** diff --git a/plugins/user-authenticators/saml2/src/test/java/org/apache/cloudstack/SAMLUtilsTest.java b/plugins/user-authenticators/saml2/src/test/java/org/apache/cloudstack/SAMLUtilsTest.java index 752845edb642..891d028aebfe 100644 --- a/plugins/user-authenticators/saml2/src/test/java/org/apache/cloudstack/SAMLUtilsTest.java +++ b/plugins/user-authenticators/saml2/src/test/java/org/apache/cloudstack/SAMLUtilsTest.java @@ -58,7 +58,7 @@ public void testBuildAuthnRequestObject() throws Exception { String idpUrl = "http://idp.domain.example"; String spId = "cloudstack"; String authnId = SAMLUtils.generateSecureRandomId(); - AuthnRequest req = SAMLUtils.buildAuthnRequestObject(authnId, spId, idpUrl, consumerUrl); + AuthnRequest req = SAMLUtils.buildAuthnRequestObject(authnId, spId, idpUrl, consumerUrl, true); assertEquals(req.getAssertionConsumerServiceURL(), consumerUrl); assertEquals(req.getDestination(), idpUrl); assertEquals(req.getIssuer().getValue(), spId); @@ -86,7 +86,7 @@ public void testBuildAuthnRequestUrlWithoutQueryParam() throws Exception { idpMetadata.setSsoUrl(idpUrl); idpMetadata.setEntityId(idpId); - URI redirectUrl = new URI(SAMLUtils.buildAuthnRequestUrl(authnId, spMetadata, idpMetadata, SAML2AuthManager.SAMLSignatureAlgorithm.value())); + URI redirectUrl = new URI(SAMLUtils.buildAuthnRequestUrl(authnId, spMetadata, idpMetadata, SAML2AuthManager.SAMLSignatureAlgorithm.value(), true)); assertThat(redirectUrl).hasScheme(urlScheme).hasHost(idpDomain).hasParameter("SAMLRequest"); assertEquals(urlScheme, redirectUrl.getScheme()); assertEquals(idpDomain, redirectUrl.getHost()); @@ -115,7 +115,7 @@ public void testBuildAuthnRequestUrlWithQueryParam() throws Exception { idpMetadata.setSsoUrl(idpUrl); idpMetadata.setEntityId(idpId); - URI redirectUrl = new URI(SAMLUtils.buildAuthnRequestUrl(authnId, spMetadata, idpMetadata, SAML2AuthManager.SAMLSignatureAlgorithm.value())); + URI redirectUrl = new URI(SAMLUtils.buildAuthnRequestUrl(authnId, spMetadata, idpMetadata, SAML2AuthManager.SAMLSignatureAlgorithm.value(), true)); assertThat(redirectUrl).hasScheme(urlScheme).hasHost(idpDomain).hasParameter("idpid").hasParameter("SAMLRequest"); assertEquals(urlScheme, redirectUrl.getScheme()); assertEquals(idpDomain, redirectUrl.getHost()); diff --git a/plugins/user-authenticators/saml2/src/test/java/org/apache/cloudstack/api/command/ListAndSwitchSAMLAccountCmdTest.java b/plugins/user-authenticators/saml2/src/test/java/org/apache/cloudstack/api/command/ListAndSwitchSAMLAccountCmdTest.java index 9342a0c7f09d..afc2f82b2060 100644 --- a/plugins/user-authenticators/saml2/src/test/java/org/apache/cloudstack/api/command/ListAndSwitchSAMLAccountCmdTest.java +++ b/plugins/user-authenticators/saml2/src/test/java/org/apache/cloudstack/api/command/ListAndSwitchSAMLAccountCmdTest.java @@ -27,6 +27,7 @@ import java.util.HashMap; import java.util.Map; +import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @@ -88,6 +89,9 @@ public class ListAndSwitchSAMLAccountCmdTest extends TestCase { @Mock HttpServletRequest req; + final String sessionId = "node0xxxxxxxxxxxxx"; + Cookie[] cookies; + @Test public void testListAndSwitchSAMLAccountCmd() throws Exception { // Setup @@ -95,6 +99,7 @@ public void testListAndSwitchSAMLAccountCmd() throws Exception { final String sessionKeyValue = "someSessionIDValue"; Mockito.when(session.getAttribute(ApiConstants.SESSIONKEY)).thenReturn(sessionKeyValue); Mockito.when(session.getAttribute("userid")).thenReturn(2L); + Mockito.when(session.getId()).thenReturn(sessionId); params.put(ApiConstants.USER_ID, new String[]{"2"}); params.put(ApiConstants.DOMAIN_ID, new String[]{"1"}); Mockito.when(userDao.findByUuid(anyString())).thenReturn(new UserVO(2L)); @@ -146,7 +151,25 @@ public void testListAndSwitchSAMLAccountCmd() throws Exception { Mockito.verify(accountService, Mockito.times(0)).getUserAccountById(Mockito.anyLong()); } - // valid sessionkey value test + // valid sessionkey value and invalid JSESSIONID test + cookies = new Cookie[2]; + cookies[0] = new Cookie(ApiConstants.SESSIONKEY, sessionKeyValue); + cookies[1] = new Cookie("JSESSIONID", "invalid-JSESSIONID"); + Mockito.when(req.getCookies()).thenReturn(cookies); + params.put(ApiConstants.SESSIONKEY, new String[]{sessionKeyValue}); + try { + cmd.authenticate("command", params, session, null, HttpUtils.RESPONSE_TYPE_JSON, new StringBuilder(), req, resp); + } catch (ServerApiException exception) { + assertEquals(exception.getErrorCode(), ApiErrorCode.UNAUTHORIZED); + } finally { + Mockito.verify(accountService, Mockito.times(0)).getUserAccountById(Mockito.anyLong()); + } + + // valid sessionkey value and valid JSESSIONID test + cookies = new Cookie[2]; + cookies[0] = new Cookie(ApiConstants.SESSIONKEY, sessionKeyValue); + cookies[1] = new Cookie("JSESSIONID", sessionId + ".node0"); + Mockito.when(req.getCookies()).thenReturn(cookies); params.put(ApiConstants.SESSIONKEY, new String[]{sessionKeyValue}); try { cmd.authenticate("command", params, session, null, HttpUtils.RESPONSE_TYPE_JSON, new StringBuilder(), req, resp); @@ -190,7 +213,6 @@ public void testListAndSwitchSAMLAccountCmd() throws Exception { loginCmdResponse.set2FAenabled("false"); Mockito.when(apiServer.loginUser(nullable(HttpSession.class), nullable(String.class), nullable(String.class), nullable(Long.class), nullable(String.class), nullable(InetAddress.class), nullable(Map.class))).thenReturn(loginCmdResponse); - Mockito.doNothing().when(resp).sendRedirect(nullable(String.class)); try { cmd.authenticate("command", params, session, null, HttpUtils.RESPONSE_TYPE_JSON, new StringBuilder(), req, resp); } catch (ServerApiException exception) { @@ -198,7 +220,6 @@ public void testListAndSwitchSAMLAccountCmd() throws Exception { } finally { // accountService should have been called 4 times by now, for this case twice and 2 for cases above Mockito.verify(accountService, Mockito.times(4)).getUserAccountById(Mockito.anyLong()); - Mockito.verify(resp, Mockito.times(1)).sendRedirect(anyString()); } } diff --git a/plugins/user-authenticators/saml2/src/test/java/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmdTest.java b/plugins/user-authenticators/saml2/src/test/java/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmdTest.java index 39c8c231bf01..426ec4e073f4 100644 --- a/plugins/user-authenticators/saml2/src/test/java/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmdTest.java +++ b/plugins/user-authenticators/saml2/src/test/java/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmdTest.java @@ -111,6 +111,9 @@ public class SAML2LoginAPIAuthenticatorCmdTest { @Mock HttpServletRequest req; + @Mock + Object _responseObject; + @Spy @InjectMocks private SAML2LoginAPIAuthenticatorCmd cmdSpy; @@ -271,6 +274,30 @@ public void whenFailToAuthenticateThrowExceptionOrRedirectToUrlTestSaml2FailedLo verifyTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl(false, hasThrownServerApiException, 0, 0); } + private void overrideDefaultConfigValue(final ConfigKey configKey, final String name, final Object o) throws IllegalAccessException, NoSuchFieldException { + Field f = ConfigKey.class.getDeclaredField(name); + f.setAccessible(true); + f.set(configKey, o); + } + + @Test + public void testFailOnSAMLSignatureCheckWhenFalse() throws NoSuchFieldException, IllegalAccessException { + overrideDefaultConfigValue(SAML2AuthManager.SAMLCheckSignature, "_value", false); + SAML2LoginAPIAuthenticatorCmd cmd = new SAML2LoginAPIAuthenticatorCmd(); + try { + cmd.checkAndFailOnMissingSAMLSignature(null); + } catch(Exception e) { + Assert.fail("This shouldn't throw any exception"); + } + } + + @Test(expected = ServerApiException.class) + public void testFailOnSAMLSignatureCheckWhenTrue() throws NoSuchFieldException, IllegalAccessException { + overrideDefaultConfigValue(SAML2AuthManager.SAMLCheckSignature, "_value", true); + SAML2LoginAPIAuthenticatorCmd cmd = new SAML2LoginAPIAuthenticatorCmd(); + cmd.checkAndFailOnMissingSAMLSignature(null); + } + private UserAccountVO configureTestWhenFailToAuthenticateThrowExceptionOrRedirectToUrl(String entity, String configurationValue, Boolean isUserAuthorized) throws IOException { Mockito.when(samlAuthManager.isUserAuthorized(nullable(Long.class), nullable(String.class))).thenReturn(isUserAuthorized); diff --git a/plugins/user-authenticators/sha256salted/pom.xml b/plugins/user-authenticators/sha256salted/pom.xml index 823ab5105686..2b47e010c26d 100644 --- a/plugins/user-authenticators/sha256salted/pom.xml +++ b/plugins/user-authenticators/sha256salted/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.0.0-SNAPSHOT + 4.23.0.0-SNAPSHOT ../../pom.xml diff --git a/plugins/user-authenticators/sha256salted/src/main/resources/META-INF/cloudstack/sha256salted/spring-sha256salted-context.xml b/plugins/user-authenticators/sha256salted/src/main/resources/META-INF/cloudstack/sha256salted/spring-sha256salted-context.xml index 3e29fd9ddba7..593c8282cb5b 100644 --- a/plugins/user-authenticators/sha256salted/src/main/resources/META-INF/cloudstack/sha256salted/spring-sha256salted-context.xml +++ b/plugins/user-authenticators/sha256salted/src/main/resources/META-INF/cloudstack/sha256salted/spring-sha256salted-context.xml @@ -30,5 +30,5 @@ - + diff --git a/plugins/user-two-factor-authenticators/static-pin/pom.xml b/plugins/user-two-factor-authenticators/static-pin/pom.xml index bde07b6c185d..522e2451e7e4 100644 --- a/plugins/user-two-factor-authenticators/static-pin/pom.xml +++ b/plugins/user-two-factor-authenticators/static-pin/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.0.0-SNAPSHOT + 4.23.0.0-SNAPSHOT ../../pom.xml diff --git a/plugins/user-two-factor-authenticators/static-pin/src/main/resources/META-INF/cloudstack/staticpin/spring-staticpin-context.xml b/plugins/user-two-factor-authenticators/static-pin/src/main/resources/META-INF/cloudstack/staticpin/spring-staticpin-context.xml index ac27ba527db6..6b30a98ef07a 100644 --- a/plugins/user-two-factor-authenticators/static-pin/src/main/resources/META-INF/cloudstack/staticpin/spring-staticpin-context.xml +++ b/plugins/user-two-factor-authenticators/static-pin/src/main/resources/META-INF/cloudstack/staticpin/spring-staticpin-context.xml @@ -31,5 +31,5 @@ - + diff --git a/plugins/user-two-factor-authenticators/totp/pom.xml b/plugins/user-two-factor-authenticators/totp/pom.xml index cda383362919..eb3cc0b1ca61 100644 --- a/plugins/user-two-factor-authenticators/totp/pom.xml +++ b/plugins/user-two-factor-authenticators/totp/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.0.0-SNAPSHOT + 4.23.0.0-SNAPSHOT ../../pom.xml diff --git a/plugins/user-two-factor-authenticators/totp/src/main/java/org/apache/cloudstack/auth/TotpUserTwoFactorAuthenticator.java b/plugins/user-two-factor-authenticators/totp/src/main/java/org/apache/cloudstack/auth/TotpUserTwoFactorAuthenticator.java index c7c4997f1894..8ea6ad20be0f 100644 --- a/plugins/user-two-factor-authenticators/totp/src/main/java/org/apache/cloudstack/auth/TotpUserTwoFactorAuthenticator.java +++ b/plugins/user-two-factor-authenticators/totp/src/main/java/org/apache/cloudstack/auth/TotpUserTwoFactorAuthenticator.java @@ -54,7 +54,9 @@ public void check2FA(String code, UserAccount userAccount) throws CloudTwoFactor logger.info("2FA matches user's input"); return; } - throw new CloudTwoFactorAuthenticationException("two-factor authentication code provided is invalid"); + String msg = "two-factor authentication code provided is invalid"; + logger.error(msg); + throw new CloudTwoFactorAuthenticationException(msg); } private String get2FAKey(UserAccount userAccount) { @@ -71,7 +73,7 @@ private String get2FACode(String secretKey) { @Override public String setup2FAKey(UserAccount userAccount) { if (StringUtils.isNotEmpty(userAccount.getKeyFor2fa())) { - throw new CloudRuntimeException(String.format("2FA key is already setup for the user account %s", userAccount.getAccountName())); + throw new CloudRuntimeException(String.format("2FA key is already setup for the user Account %s", userAccount.getAccountName())); } SecureRandom random = new SecureRandom(); byte[] bytes = new byte[20]; diff --git a/plugins/user-two-factor-authenticators/totp/src/main/resources/META-INF/cloudstack/totp/spring-google-context.xml b/plugins/user-two-factor-authenticators/totp/src/main/resources/META-INF/cloudstack/totp/spring-google-context.xml index 84a0b0cfcb7c..73ab7b636d44 100644 --- a/plugins/user-two-factor-authenticators/totp/src/main/resources/META-INF/cloudstack/totp/spring-google-context.xml +++ b/plugins/user-two-factor-authenticators/totp/src/main/resources/META-INF/cloudstack/totp/spring-google-context.xml @@ -31,5 +31,5 @@ - + diff --git a/pom.xml b/pom.xml index 3030302c24e7..17935c526922 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ org.apache.cloudstack cloudstack - 4.20.0.0-SNAPSHOT + 4.23.0.0-SNAPSHOT pom Apache CloudStack Apache CloudStack is an IaaS ("Infrastructure as a Service") cloud orchestration platform. @@ -50,35 +50,38 @@ UTF-8 UTF-8 https://download.cloudstack.org/systemvm - 4.19.0.0 + 4.22.0.0 apache https://sonarcloud.io + engine/schema/src/main/java/org/apache/cloudstack/backup/BackupOfferingDetailsVO.java + api/src/main/java/org/apache/cloudstack/api/response/BackupOfferingResponse.java 11 target build/replace.properties - -Djava.security.egd=file:/dev/./urandom -noverify + -Djava.security.egd=file:/dev/./urandom -noverify --add-opens=java.base/java.lang=ALL-UNNAMED --add-exports=java.base/sun.security.x509=ALL-UNNAMED --add-exports=java.base/sun.security.provider=ALL-UNNAMED --add-opens=java.base/javax.net.ssl=ALL-UNNAMED 1.8 3.0.0 - 3.1.0 + 3.6.0 0.8.11 3.8.1 - 3.1.1 + 3.9.0 2.22.2 3.1.12 3.1.12.2 3.2.0 - 3.12.0 + 3.28.0 3.0.0 7.4.4 2.5.3 3.1.0 - 3.8.2 + 3.21.0 2.22.2 4.4.1 + 3.2.0 2.19.0 @@ -86,10 +89,10 @@ 1.2.17 - 1.15 + 1.20.0 1.5.0 4.4 - 1.21 + 1.26.0 1.3 1.4 3.1 @@ -101,6 +104,7 @@ 1.10 1.3.3 2.9.0 + 7.0.2 0.5 2.6 2.9.0 @@ -114,9 +118,7 @@ 1.13.1 5.9.1 18.0 - 4.11.0 - 1.0-20081010.060147 - 1.0.1 + 5.16.1 7.1.0 2.27.2 2.12.2 @@ -126,7 +128,7 @@ 5.17.0 1.0.9 1.9.19 - 1.12.439 + 1.12.795 1.2.8 1.6.4 1.14 @@ -137,13 +139,14 @@ 3.2.14 2.6.11 0.0.27 + 3.0.2 1.42.3 2.4.17 - 1.7.2 + 2.10.1 31.1-jre 4.5.14 4.4.16 - 2.22 + 2.25 2.13.3 1.9.3 0.17 @@ -151,25 +154,28 @@ 3.1.1 1.3.2 2.3.0 - 2.3.2-1 + 2.3.9 + 2.3.3 + 2.3.7 2.26 - 9.4.51.v20230217 + 9.4.58.v20250814 9.4.27.v20200227 5.5.0 2.12.5 2.2.1 0.1.55 - 20090211 + 20231013 1.2 2.7.0 0.5.3 1.5.0-b01 - 8.0.19 + 0.9.14 + 8.0.33 2.0.4 10.1 2.6.6 0.6.0 - 0.3.0 + 0.6.1 0.10.2 3.4.4_1 4.0.1 @@ -178,12 +184,13 @@ build-217-jenkins-27 8.0 0.5.0 - 6.2.0-3.1 + 8.1.0 3.1.3 1.4.20 5.3.26 0.5.4 - 1.12.0 + 3.1.7 + 3.25.5 @@ -289,6 +296,11 @@ aws-java-sdk-core ${cs.aws.sdk.version} + + com.amazonaws + aws-java-sdk-iam + ${cs.aws.sdk.version} + com.amazonaws aws-java-sdk-s3 @@ -360,6 +372,7 @@ commons-daemon ${cs.daemon.version} + org.apache.commons commons-dbcp2 @@ -371,6 +384,11 @@ + + com.zaxxer + HikariCP + ${cs.hikaricp.version} + commons-discovery commons-discovery @@ -451,6 +469,12 @@ reload4j ${cs.reload4j.version} + + com.mysql + mysql-connector-j + ${cs.mysql.version} + test + log4j apache-log4j-extras @@ -462,12 +486,6 @@ - - mysql - mysql-connector-java - ${cs.mysql.version} - test - net.sf.ehcache ehcache-core @@ -709,6 +727,17 @@ xml-apis 2.0.2 + + + com.google.protobuf + protobuf-java + ${cs.protobuf.version} + + + com.google.protobuf + protobuf-java-util + ${cs.protobuf.version} + com.linbit.linstor.api java-linstor @@ -727,7 +756,7 @@ org.mockito - mockito-inline + mockito-core ${cs.mockito.version} test @@ -766,6 +795,11 @@ javax.inject 1 + + com.github.ben-manes.caffeine + caffeine + ${cs.caffeine.version} + @@ -931,20 +965,6 @@ - - - org.codehaus.gmaven - gmaven-plugin - [1.3,) - - compile - testCompile - - - - - - org.apache.maven.plugins @@ -1004,18 +1024,17 @@ **/.vagrant **/*.json **/.checkstyle - **/*.md .java-version .python-version - systemvm/.pythen-version .idea/ .metadata/** .git/** + .github/linters/codespell.txt .gitignore + .markdownlintignore CHANGES.md - CONTRIBUTING.md - README.md - INSTALL.md + ISSUE_TEMPLATE.md + PULL_REQUEST_TEMPLATE.md build/build.number debian/cloudstack-agent.dirs debian/cloudstack-usage.dirs @@ -1027,22 +1046,26 @@ dist/console-proxy/js/jquery.js engine/schema/dist/** plugins/hypervisors/hyperv/conf/agent.properties + plugins/hypervisors/hyperv/conf/uefi.properties plugins/hypervisors/hyperv/DotNet/ServerResource/** scripts/installer/windows/acs_license.rtf scripts/vm/systemvm/id_rsa.cloud services/console-proxy/server/conf/agent.properties + services/console-proxy/server/conf/uefi.properties services/console-proxy/server/conf/environment.properties services/console-proxy/server/js/jquery.js services/secondary-storage/conf/agent.properties + services/secondary-storage/conf/uefi.properties services/secondary-storage/conf/environment.properties systemvm/agent/conf/agent.properties + systemvm/agent/conf/uefi.properties systemvm/agent/conf/environment.properties systemvm/agent/js/jquery.js systemvm/agent/js/jquery.flot.navigate.js systemvm/agent/noVNC/** + systemvm/agent/packages/** systemvm/debian/** test/integration/component/test_host_ha.sh - test/systemvm/README.md tools/appliance/*/template.json tools/cli/cloudmonkey.egg-info/* tools/devcloud/basebuild/puppet-devcloudinitial/files/network.conf @@ -1056,11 +1079,18 @@ tools/transifex/.tx/config ui/.* ui/.*/** + ui/docs/full-test-plan.template.md + ui/docs/smoke-test-plan.template.md ui/src/assets/** ui/public/** ui/legacy/** utils/testsmallfileinactive **/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker + .github/workflows/dependabot.yaml + .gitattributes + .github/workflows/*.md + .github/workflows/*.lock.ya?ml + .github/aw/** diff --git a/python/bindir/cloud-grab-dependent-library-versions b/python/bindir/cloud-grab-dependent-library-versions index cdb50531ed39..40010fd99aae 100755 --- a/python/bindir/cloud-grab-dependent-library-versions +++ b/python/bindir/cloud-grab-dependent-library-versions @@ -6,9 +6,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 @@ -18,64 +18,99 @@ import subprocess -depLibraries = ['bzip2', 'gzip', 'unzip', 'openssh-clients', 'nfs-utils', 'wget', 'ws-commons-util', 'commons-dbcp', - 'commons-collections', 'commons-httpclient', 'jpackage-utils', 'mysql-connector-python3', 'python-paramiko', 'ipmitool', 'commons-httpclient', 'commons-collections', - 'commons-pool', 'commons-dbcp', 'jakarta-commons-logging', 'java-*-openjdk'] +depLibraries = [ + "bzip2", + "gzip", + "unzip", + "openssh-clients", + "nfs-utils", + "wget", + "ws-commons-util", + "commons-dbcp", + "commons-collections", + "commons-httpclient", + "jpackage-utils", + "mysql-connector-python3", + "python-paramiko", + "ipmitool", + "commons-httpclient", + "commons-collections", + "commons-pool", + "commons-dbcp", + "jakarta-commons-logging", + "java-*-openjdk", +] + def runCmd(cmds): - process = subprocess.Popen(' '.join(cmds), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + process = subprocess.Popen( + " ".join(cmds), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) stdout, stderr = process.communicate() if process.returncode != 0: - raise Exception(stderr.decode('utf-8')) - return stdout.decode('utf-8') + raise Exception(stderr.decode("utf-8")) + return stdout.decode("utf-8") def getDependentLibraryInfo(): def getVersion(res, pkgname): start = False - for l in res.split('\n'): + for l in res.split("\n"): if "Installed Packages" in l: start = True continue - if not start: continue - - (key, value) = l.split(':', 2) + if not start: + continue + + (key, value) = l.split(":", 2) key = key.strip() value = value.strip() - if key == 'Name' and "*" not in pkgname and pkgname not in value: - print("Required package name %s doesn't equal to package %s installed"%(pkgname, value)) - return 'UNKNOWN' - if 'Version' in key: return value - if 'Description' in key: return 'UNKNOWN' # we hit the end - return 'UNKNOWN' + if key == "Name" and "*" not in pkgname and pkgname not in value: + print( + "Required package name %s doesn't equal to package %s installed" + % (pkgname, value) + ) + return "UNKNOWN" + if "Version" in key: + return value + if "Description" in key: + return "UNKNOWN" # we hit the end + return "UNKNOWN" libraryMap = {} for l in depLibraries: - cmd = ['yum', 'info', '"%s"'%l] + cmd = ["yum", "info", '"%s"' % l] try: result = runCmd(cmd) version = getVersion(result, l) libraryMap[l] = version except Exception as e: - print("When finding %s, encounters %s"%(l, e)) + print("When finding %s, encounters %s" % (l, e)) continue return libraryMap + def arrangeOutPut(libraryMap): - msg = ['\n\n\nBelow is the checking list of library version that CloudStack depends on:'] + msg = [ + "\n\n\nBelow is the checking list of library version that CloudStack depends on:" + ] for l in depLibraries: if l in libraryMap: - entry = "%-40s: %s"%(l, libraryMap[l]) + entry = "%-40s: %s" % (l, libraryMap[l]) else: - entry = "%-40s: %s"%(l, 'UNKNOWN') + entry = "%-40s: %s" % (l, "UNKNOWN") msg.append(entry) - print('\n'.join(msg)) - -if __name__ == '__main__': - pythonDepLibraries = ['python', 'python3'] - relver = runCmd(['rpm', '-q', 'centos-release']) - if relver.startswith('centos-release-') and int(relver[len('centos-release-')]) >= 8: - pythonDepLibraries = ['python2', 'python36'] + print("\n".join(msg)) + + +if __name__ == "__main__": + pythonDepLibraries = ["python", "python3"] + relver = runCmd(["rpm", "-q", "centos-release"]) + if ( + relver.startswith("centos-release-") + and int(relver[len("centos-release-")]) >= 8 + ): + pythonDepLibraries = ["python2", "python36"] depLibraries = pythonDepLibraries + depLibraries arrangeOutPut(getDependentLibraryInfo()) diff --git a/python/bindir/cloud-setup-baremetal b/python/bindir/cloud-setup-baremetal index a4ef7add4cd8..b953ded7b64e 100755 --- a/python/bindir/cloud-setup-baremetal +++ b/python/bindir/cloud-setup-baremetal @@ -6,9 +6,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 @@ -23,11 +23,15 @@ import traceback from os.path import exists, join from signal import alarm, signal, SIGALRM, SIGKILL + class CloudRuntimeException(Exception): def __init__(self, errMsg): self.errMsg = errMsg + def __str__(self): return self.errMsg + + def formatExceptionInfo(maxTBlevel=5): cla, exc, trbk = sys.exc_info() excTb = traceback.format_tb(trbk, maxTBlevel) @@ -36,10 +40,11 @@ def formatExceptionInfo(maxTBlevel=5): msg += tb return msg + class bash: def __init__(self, args, timeout=600): self.args = args - logging.debug("execute:%s"%args) + logging.debug("execute:%s" % args) self.timeout = timeout self.process = None self.success = False @@ -48,6 +53,7 @@ class bash: def run(self): class Alarm(Exception): pass + def alarm_handler(signum, frame): raise Alarm @@ -63,21 +69,21 @@ class bash: alarm(0) except Alarm: os.kill(self.process.pid, SIGKILL) - raise CloudRuntimeException("Timeout during command execution") + raise CloudRuntimeException("Timeout during command execution") self.success = self.process.returncode == 0 except: - raise CloudRuntimeException(formatExceptionInfo()) + raise CloudRuntimeException(formatExceptionInfo()) -# if not self.success: -# raise CloudRuntimeException(self.getStderr()) + # if not self.success: + # raise CloudRuntimeException(self.getStderr()) def isSuccess(self): return self.success - + def getStdout(self): return self.stdout.strip("\n") - + def getLines(self): return self.stdout.split("\n") @@ -88,130 +94,150 @@ class bash: def initLoging(logFile=None): try: if logFile is None: - logging.basicConfig(level=logging.DEBUG) - else: - logging.basicConfig(filename=logFile, level=logging.DEBUG) + logging.basicConfig(level=logging.DEBUG) + else: + logging.basicConfig(filename=logFile, level=logging.DEBUG) except: - logging.basicConfig(level=logging.DEBUG) + logging.basicConfig(level=logging.DEBUG) + -def writeProgressBar(msg, result=None): +def writeProgressBar(msg, result=None): if msg is not None: - output = "%-80s"%msg + output = "%-80s" % msg elif result is True: - output = "[ \033[92m%-2s\033[0m ]\n"%"OK" + output = "[ \033[92m%-2s\033[0m ]\n" % "OK" elif result is False: - output = "[ \033[91m%-6s\033[0m ]\n"%"FAILED" + output = "[ \033[91m%-6s\033[0m ]\n" % "FAILED" sys.stdout.write(output) sys.stdout.flush() - + + def printError(msg): sys.stderr.write(msg) sys.stderr.write("\n") sys.stderr.flush() + def printMsg(msg): - sys.stdout.write(msg+"\n") + sys.stdout.write(msg + "\n") sys.stdout.flush() + def checkRpm(pkgName): - chkPkg = bash("rpm -q %s"%pkgName) - writeProgressBar("Checking %s"%pkgName, None) + chkPkg = bash("rpm -q %s" % pkgName) + writeProgressBar("Checking %s" % pkgName, None) if not chkPkg.isSuccess(): writeProgressBar(None, False) - printError("%s is not found, please make sure it is installed. You may try 'yum install %s'\n"%(pkgName, pkgName)) + printError( + "%s is not found, please make sure it is installed. You may try 'yum install %s'\n" + % (pkgName, pkgName) + ) return False else: writeProgressBar(None, True) return True - + + def checkEnv(): - writeProgressBar("Checking is root") - ret = bash("whoami") - if ret.getStdout() != "root": - writeProgressBar(None, False) - printError("This script must run as root") - return False - else: - writeProgressBar(None, True) - - pkgList = ['tftp-server', 'syslinux', 'xinetd', 'chkconfig', 'dhcp'] - for pkg in pkgList: - if not checkRpm(pkg): - return False - return True + writeProgressBar("Checking is root") + ret = bash("whoami") + if ret.getStdout() != "root": + writeProgressBar(None, False) + printError("This script must run as root") + return False + else: + writeProgressBar(None, True) + + pkgList = ["tftp-server", "syslinux", "xinetd", "chkconfig", "dhcp"] + for pkg in pkgList: + if not checkRpm(pkg): + return False + return True + def exitIfFail(ret): - if not ret: sys.exit(1) - + if not ret: + sys.exit(1) + + def bashWithResult(cmd): - writeProgressBar("Executing '%s'"%cmd) + writeProgressBar("Executing '%s'" % cmd) ret = bash(cmd) if not ret.isSuccess(): writeProgressBar(None, False) - writeProgressBar(ret.getStderr() + '\n') + writeProgressBar(ret.getStderr() + "\n") return False else: writeProgressBar(None, True) return True - -def configurePxeStuff(): - stuff = ['tftp', 'xinetd', 'dhcpd'] - cmds = ['chkconfig --level 345 %s on' % i for i in stuff] - cmds.append('/etc/init.d/xinetd restart') - + + +def configurePxeStuff(): + stuff = ["tftp", "xinetd", "dhcpd"] + cmds = ["chkconfig --level 345 %s on" % i for i in stuff] + cmds.append("/etc/init.d/xinetd restart") + for cmd in cmds: - if not bashWithResult(cmd): return False - - chkIptable = bash('chkconfig --list iptables') - if 'on' in chkIptable.getStdout(): + if not bashWithResult(cmd): + return False + + chkIptable = bash("chkconfig --list iptables") + if "on" in chkIptable.getStdout(): printMsg("Detected iptables is running, need to open tftp port 69") - if not bashWithResult('iptables -I INPUT 1 -p udp --dport 69 -j ACCEPT'): return False - if not bashWithResult('/etc/init.d/iptables save'): return False - - return True - + if not bashWithResult("iptables -I INPUT 1 -p udp --dport 69 -j ACCEPT"): + return False + if not bashWithResult("/etc/init.d/iptables save"): + return False + + return True + + def getTftpRootDir(tftpRootDirList): tftpRoot = bash("cat /etc/xinetd.d/tftp | grep server_args") if not tftpRoot.isSuccess(): - printError("Cannot get tftp root directory from /etc/xinetd.d/tftp, here may be something wrong with your tftp-server, try reinstall it\n") + printError( + "Cannot get tftp root directory from /etc/xinetd.d/tftp, here may be something wrong with your tftp-server, try reinstall it\n" + ) return False tftpRootDir = tftpRoot.getStdout() index = tftpRootDir.find("/") if index == -1: - printError("Wrong server_arg in /etc/xinetd.d/tftp (%s)"%tftpRootDir) + printError("Wrong server_arg in /etc/xinetd.d/tftp (%s)" % tftpRootDir) return False tftpRootDir = tftpRootDir[index:] tftpRootDirList.append(tftpRootDir) return True + def preparePING(tftpRootDir): - pingFiles = ['boot.msg', 'initrd.gz', 'kernel', 'pxelinux.0'] + pingFiles = ["boot.msg", "initrd.gz", "kernel", "pxelinux.0"] pingDir = "/usr/share/PING" - + for f in pingFiles: path = join(pingDir, f) if not exists(path): - printError("Cannot find %s, please make sure PING-3.01 is installed"%path) + printError("Cannot find %s, please make sure PING-3.01 is installed" % path) return False - if not bashWithResult("cp -f %s %s"%(path, tftpRootDir)): return False - - if not bashWithResult("mkdir -p %s/pxelinux.cfg"%tftpRootDir): return False - + if not bashWithResult("cp -f %s %s" % (path, tftpRootDir)): + return False + + if not bashWithResult("mkdir -p %s/pxelinux.cfg" % tftpRootDir): + return False + return True - - + + if __name__ == "__main__": initLoging("/tmp/cloud-setup-baremetal.log") tftpRootDirList = [] - + exitIfFail(checkEnv()) exitIfFail(configurePxeStuff()) exitIfFail(getTftpRootDir(tftpRootDirList)) - + tftpRootDir = tftpRootDirList[0].strip() exitIfFail(preparePING(tftpRootDir)) printMsg("") printMsg("Setup BareMetal PXE server successfully") - printMsg("TFTP root directory is: %s\n"%tftpRootDir) + printMsg("TFTP root directory is: %s\n" % tftpRootDir) sys.exit(0) - diff --git a/python/distro/centos/SYSCONFDIR/rc.d/init.d/cloud-ipallocator.in b/python/distro/centos/SYSCONFDIR/rc.d/init.d/cloud-ipallocator.in index 4944fc0218e1..546ec1cff771 100755 --- a/python/distro/centos/SYSCONFDIR/rc.d/init.d/cloud-ipallocator.in +++ b/python/distro/centos/SYSCONFDIR/rc.d/init.d/cloud-ipallocator.in @@ -6,9 +6,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/python/distro/fedora/SYSCONFDIR/rc.d/init.d/cloud-ipallocator.in b/python/distro/fedora/SYSCONFDIR/rc.d/init.d/cloud-ipallocator.in index 28d5a115914c..6811c2191c7b 100755 --- a/python/distro/fedora/SYSCONFDIR/rc.d/init.d/cloud-ipallocator.in +++ b/python/distro/fedora/SYSCONFDIR/rc.d/init.d/cloud-ipallocator.in @@ -6,9 +6,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/python/distro/opensuse/SYSCONFDIR/init.d/cloud-ipallocator.in b/python/distro/opensuse/SYSCONFDIR/init.d/cloud-ipallocator.in index de68534f2e43..9d201a2a13e4 100755 --- a/python/distro/opensuse/SYSCONFDIR/init.d/cloud-ipallocator.in +++ b/python/distro/opensuse/SYSCONFDIR/init.d/cloud-ipallocator.in @@ -6,9 +6,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/python/distro/rhel/SYSCONFDIR/rc.d/init.d/cloud-ipallocator.in b/python/distro/rhel/SYSCONFDIR/rc.d/init.d/cloud-ipallocator.in index 28d5a115914c..6811c2191c7b 100644 --- a/python/distro/rhel/SYSCONFDIR/rc.d/init.d/cloud-ipallocator.in +++ b/python/distro/rhel/SYSCONFDIR/rc.d/init.d/cloud-ipallocator.in @@ -6,9 +6,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/python/distro/sles/SYSCONFDIR/init.d/cloud-ipallocator.in b/python/distro/sles/SYSCONFDIR/init.d/cloud-ipallocator.in index de68534f2e43..9d201a2a13e4 100755 --- a/python/distro/sles/SYSCONFDIR/init.d/cloud-ipallocator.in +++ b/python/distro/sles/SYSCONFDIR/init.d/cloud-ipallocator.in @@ -6,9 +6,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/python/distro/ubuntu/SYSCONFDIR/init.d/cloud-ipallocator.in b/python/distro/ubuntu/SYSCONFDIR/init.d/cloud-ipallocator.in index 4acb11a1f4f1..f9a855b26d12 100755 --- a/python/distro/ubuntu/SYSCONFDIR/init.d/cloud-ipallocator.in +++ b/python/distro/ubuntu/SYSCONFDIR/init.d/cloud-ipallocator.in @@ -6,9 +6,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 @@ -52,7 +52,7 @@ start() { log_end_msg 1 exit 1 fi - + if start-stop-daemon --start --quiet \ --pidfile "$PIDFILE" \ --exec "$DAEMONIZE" -- -n "$SHORTNAME" -p "$PIDFILE" -l "$LOGFILE" "$PROG" $OPTIONS diff --git a/python/lib/cloud_utils.py b/python/lib/cloud_utils.py index d424bf1f023e..16d9f17e8b44 100644 --- a/python/lib/cloud_utils.py +++ b/python/lib/cloud_utils.py @@ -375,7 +375,7 @@ def list_zonespods(host): x = [ (zonename,podname) for pod in dom.childNodes[0].childNodes for podname in [ x.childNodes[0].wholeText for x in pod.childNodes if x.tagName == "name" ] - for zonename in [ x.childNodes[0].wholeText for x in pod.childNodes if x.tagName == "zonename" ] + for zonename in [ x.childNodes[0].wholeText for x in pod.childNodes if x.tagName == "zonename" ] ] return x @@ -433,7 +433,7 @@ def setup_agent_config(configfile, host, zone, pod, cluster, guid, pubNic, prvNi if guid != None: confopts['guid'] = guid else: - if not "guid" in confopts: + if "guid" not in confopts: stderr("Generating GUID for this Agent") confopts['guid'] = uuidgen().stdout.strip() @@ -491,7 +491,7 @@ def setup_consoleproxy_config(configfile, host, zone, pod): confopts = dict([ m.split("=",1) for m in lines if "=" in m and not m.startswith("#") ]) confposes = dict([ (m.split("=",1)[0],n) for n,m in enumerate(lines) if "=" in m and not m.startswith("#") ]) - if not "guid" in confopts: + if "guid" not in confopts: stderr("Generating GUID for this Console Proxy") confopts['guid'] = uuidgen().stdout.strip() diff --git a/python/lib/cloudutils/configFileOps.py b/python/lib/cloudutils/configFileOps.py index 41e9c7a1e8eb..f4d704af783f 100644 --- a/python/lib/cloudutils/configFileOps.py +++ b/python/lib/cloudutils/configFileOps.py @@ -63,19 +63,19 @@ def save(self): newLines = [] if os.path.exists(self.fileName) and os.path.isfile(self.fileName): fp = open(self.fileName, "r") - for line in fp.readlines(): + for line in fp.readlines(): matched = False for entry in self.entries: if entry.op == "add": if entry.separator == "=": - matchString = "^\ *" + entry.name + ".*" + matchString = r"^\ *" + entry.name + ".*" elif entry.separator == " ": - matchString = "^\ *" + entry.name + "\ *" + entry.value + matchString = r"^\ *" + entry.name + r"\ *" + entry.value else: if entry.separator == "=": - matchString = "^\ *" + entry.name + "\ *=\ *" + entry.value + matchString = r"^\ *" + entry.name + r"\ *=\ *" + entry.value else: - matchString = "^\ *" + entry.name + "\ *" + entry.value + matchString = r"^\ *" + entry.name + r"\ *" + entry.value match = re.match(matchString, line) if match is not None: diff --git a/python/lib/cloudutils/networkConfig.py b/python/lib/cloudutils/networkConfig.py index 2d1f9936d394..827664bbc738 100644 --- a/python/lib/cloudutils/networkConfig.py +++ b/python/lib/cloudutils/networkConfig.py @@ -41,12 +41,15 @@ def listNetworks(): return devs @staticmethod def getDefaultNetwork(): - cmd = bash("route -n|awk \'/^0.0.0.0/ {print $2,$8}\'") + cmd = bash("ip route show default | awk \'{print $3,$5}\'") if not cmd.isSuccess(): logging.debug("Failed to get default route") raise CloudRuntimeException("Failed to get default route") - - result = cmd.getStdout().split(" ") + output = cmd.getStdout().strip() + result = output.split(" ") + if len(result) < 2: + logging.debug("Output for the default route incomplete: %s. It should have been ' '" % output) + raise CloudRuntimeException("Output for the default route incomplete") gateway = result[0] dev = result[1] @@ -150,10 +153,10 @@ def getDevInfo(dev): if line.find("HWaddr") != -1: macAddr = line.split("HWaddr ")[1].strip(" ") elif line.find("inet ") != -1: - m = re.search("addr:(.*)\ *Bcast:(.*)\ *Mask:(.*)", line) + m = re.search(r"addr:([^\s]+)\s*Bcast:([^\s]+)\s*Mask:([^\s]+)", line) if m is not None: - ipAddr = m.group(1).rstrip(" ") - netmask = m.group(3).rstrip(" ") + ipAddr = m.group(1).strip() + netmask = m.group(3).strip() if networkConfig.isBridgePort(dev): type = "brport" diff --git a/python/lib/cloudutils/syscfg.py b/python/lib/cloudutils/syscfg.py index 19032ce4affa..78cd58bfbd40 100755 --- a/python/lib/cloudutils/syscfg.py +++ b/python/lib/cloudutils/syscfg.py @@ -39,11 +39,11 @@ def getAgent(glbEnv): return sysConfigAgentUbuntu(glbEnv) elif distribution == "CentOS" or distribution == "RHEL5": return sysConfigEL5(glbEnv) - elif distribution == "Fedora" or distribution == "RHEL6": + elif distribution == "RHEL6": return sysConfigEL6(glbEnv) elif distribution == "RHEL7": return sysConfigEL7(glbEnv) - elif distribution in ["RHEL8", "RHEL9"]: + elif distribution in ["Fedora", "RHEL8", "RHEL9", "RHEL10"]: return sysConfigEL(glbEnv) elif distribution == "SUSE": return sysConfigSUSE(glbEnv) @@ -114,7 +114,7 @@ def check(self): pass if size != -1 and size < (30 * 1024 * 1024): - raise CloudRuntimeException("Need at least 30G free disk space under /var/lib/libvirt/images") + raise CloudRuntimeException("Need at least 30G free disk space under /var/lib/libvirt/images") #check memory mem = -1 @@ -124,7 +124,7 @@ def check(self): pass if mem != -1 and mem < 1: - raise CloudRuntimeException("Need at least 1G memory") + raise CloudRuntimeException("Need at least 1G memory") if os.geteuid() != 0: @@ -183,9 +183,10 @@ def __init__(self, glbEnv): networkConfigRedhat(self), libvirtConfigRedhat(self), firewallConfigAgent(self), + nfsConfig(self), cloudAgentConfig(self)] -#it covers RHEL6/Fedora13/Fedora14 +#it covers RHEL6 class sysConfigEL6(sysConfigAgentRedhatBase): def __init__(self, glbEnv): super(sysConfigEL6, self).__init__(glbEnv) diff --git a/python/lib/cloudutils/utilities.py b/python/lib/cloudutils/utilities.py index 5a6114f1011f..31f35ee6d449 100755 --- a/python/lib/cloudutils/utilities.py +++ b/python/lib/cloudutils/utilities.py @@ -47,11 +47,11 @@ def alarm_handler(signum, frame): alarm(0) except Alarm: os.kill(self.process.pid, SIGKILL) - raise CloudRuntimeException("Timeout during command execution") + raise CloudRuntimeException("Timeout during command execution") self.success = self.process.returncode == 0 except: - raise CloudRuntimeException(formatExceptionInfo()) + raise CloudRuntimeException(formatExceptionInfo()) if not self.success: logging.debug("Failed to execute:" + self.getErrMsg()) @@ -63,7 +63,7 @@ def getStdout(self): return self.stdout.decode('utf-8').strip('\n') def getLines(self): - return self.stdout.decode('utf-8').strip('\n') + return self.stdout.decode('utf-8').strip('\n').split('\n') def getStderr(self): return self.stderr.decode('utf-8').strip('\n') @@ -98,7 +98,7 @@ def writeProgressBar(msg, result): sys.stdout.flush() class UnknownSystemException(Exception): - "This Excption is raised if the current operating enviornment is unknown" + "This Exception is raised if the current operating environment is unknown" pass class Distribution: @@ -124,6 +124,10 @@ def __init__(self): version.find("Red Hat Enterprise Linux release 9") != -1 or version.find("Linux release 9.") != -1 or version.find("Linux release 9") != -1): self.distro = "RHEL9" + elif (version.find("Red Hat Enterprise Linux Server release 10") != -1 or version.find("Scientific Linux release 10") != -1 or + version.find("Red Hat Enterprise Linux release 10") != -1 or version.find("Linux release 10.") != -1 or + version.find("Linux release 10") != -1): + self.distro = "RHEL10" elif version.find("CentOS") != -1: self.distro = "CentOS" else: diff --git a/quickcloud/pom.xml b/quickcloud/pom.xml index c02b4932ee7f..cc0bacbd7e77 100644 --- a/quickcloud/pom.xml +++ b/quickcloud/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.0.0-SNAPSHOT + 4.23.0.0-SNAPSHOT ../pom.xml diff --git a/quickcloud/src/main/resources/META-INF/cloudstack/core/spring-quickcloud-core-context-override.xml b/quickcloud/src/main/resources/META-INF/cloudstack/core/spring-quickcloud-core-context-override.xml index 6074ea1ceb2e..a434bbe24127 100644 --- a/quickcloud/src/main/resources/META-INF/cloudstack/core/spring-quickcloud-core-context-override.xml +++ b/quickcloud/src/main/resources/META-INF/cloudstack/core/spring-quickcloud-core-context-override.xml @@ -25,7 +25,7 @@ http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" - > + > diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 000000000000..99ad3943160d --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +pre-commit diff --git a/requirements.txt b/requirements.txt index 96f8c9c1e70b..9711afad3d86 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,7 +17,5 @@ # Marvin dependencies are installed via its bundle -pre-commit - # Install the SolidFire SDK for Python solidfire-sdk-python diff --git a/scripts/installer/cloudstack-help-text b/scripts/installer/cloudstack-help-text index 1231464aa569..fbb2f0f0a7ee 100755 --- a/scripts/installer/cloudstack-help-text +++ b/scripts/installer/cloudstack-help-text @@ -40,4 +40,10 @@ printf " * Release notes: https://docs.cloudstack.apache.org/en/${ACL_MINO printf " * Join mailing lists: https://cloudstack.apache.org/mailing-lists.html\n" printf " * Take the survey: https://cloudstack.apache.org/survey.html\n" printf " * Report issues: https://github.com/apache/cloudstack/issues/new\n" + +if [ "$1" = "management" ];then + printf "\nSince Apache CloudStack 4.20.0.0, the System VMs and virtual routers require at least 512 MiB memory, please check the System Offerings." + printf "\nMore information can be found at https://docs.cloudstack.apache.org/en/${ACL_MINOR_VERSION:-latest}/upgrading/upgrade/_sysvm_restart.html\n" +fi + printf "\n" diff --git a/scripts/installer/createtmplt.sh b/scripts/installer/createtmplt.sh index 2d164304604e..8d9b9876700f 100755 --- a/scripts/installer/createtmplt.sh +++ b/scripts/installer/createtmplt.sh @@ -6,9 +6,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 @@ -51,7 +51,7 @@ verify_cksum() { esac echo "$1 $2" | $digestalgo -c --status #printf "$1\t$2" | $digestalgo -c --status - if [ $? -gt 0 ] + if [ $? -gt 0 ] then printf "Checksum failed, not proceeding with install\n" exit 3 @@ -87,17 +87,19 @@ uncompress() { ;; [zZ][iI][pP]) unzip -p $1 | cat > $tmpfile ;; + XZ) xz -d -c $1 > $tmpfile + ;; *) printf "$1" return 0 ;; esac - if [ $? -gt 0 ] + if [ $? -gt 0 ] then printf "Failed to uncompress file, exiting " - exit 1 + exit 1 fi - + mv $tmpfile $imgfile printf "$imgfile" @@ -184,10 +186,10 @@ then tmpltfs=${tmpltfs:1} fi -if [ ! -d /$tmpltfs ] +if [ ! -d /$tmpltfs ] then zfs create -p $tmpltfs - if [ $? -gt 0 ] + if [ $? -gt 0 ] then printf "Failed to create user fs $tmpltfs\n" >&2 exit 1 @@ -203,7 +205,7 @@ fi tmpltimg2=$(uncompress $tmpltimg) tmpltimg2=$(untar $tmpltimg2 /$tmpltfs vmi-root) -if [ ! -f $tmpltimg2 ] +if [ ! -f $tmpltimg2 ] then rollback_if_needed $tmpltfs 2 "root disk file $tmpltimg doesn't exist\n" exit 3 @@ -217,15 +219,15 @@ fi #determine source file size -- it needs to be less than or equal to volsize imgsize=$(ls -lh $tmpltimg2| awk -F" " '{print $5}') -if [ ${imgsize:(-1)} == G ] +if [ ${imgsize:(-1)} == G ] then - imgsize=${imgsize%G} #strip out the G + imgsize=${imgsize%G} #strip out the G imgsize=${imgsize%.*} #...and any decimal part let imgsize=imgsize+1 # add 1 to compensate for decimal part volsizetmp=${volsize%G} if [ $volsizetmp -lt $imgsize ] then - volsize=${imgsize}G + volsize=${imgsize}G fi fi @@ -234,11 +236,11 @@ tgtfile=${tmpltfs}/vmi-root-${tmpltname} create_from_file $tmpltfs $tmpltimg2 $tgtfile $volsize $cleanup tmpltswap=$(ls -lh /$tmpltfs | grep swap) -if [ $? -eq 0 ] +if [ $? -eq 0 ] then swapsize=$(echo $tmpltswap | awk '{print $5}') tmpltswap=$(echo $tmpltswap | awk '{print $NF}') - tmpltswap=/${tmpltfs}/${tmpltswap} + tmpltswap=/${tmpltfs}/${tmpltswap} tgtfile=${tmpltfs}/vmi-swap-${tmpltname} create_from_file $tmpltfs $tmpltswap $tgtfile $swapsize $cleanup fi @@ -270,9 +272,4 @@ echo "volume.size=$volsize" >> /$tmpltfs/template.properties zfs snapshot -r $tmpltfs@vmops_ss rollback_if_needed $tmpltfs $? "Failed to snapshot filesystem" -#if [ "$cleanup" == "true" ] -#then - #rm -f $tmpltimg -#fi - exit 0 diff --git a/scripts/installer/createvolume.sh b/scripts/installer/createvolume.sh index 15847c52375e..716bb8556ce3 100755 --- a/scripts/installer/createvolume.sh +++ b/scripts/installer/createvolume.sh @@ -6,9 +6,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 @@ -88,17 +88,19 @@ uncompress() { ;; ZIP) unzip -p $1 | cat > $tmpfile ;; + XZ) xz -d -c $1 > $tmpfile + ;; *) printf "$1" return 0 ;; esac - if [ $? -gt 0 ] + if [ $? -gt 0 ] then printf "Failed to uncompress file, exiting " - exit 1 + exit 1 fi - + mv $tmpfile $imgfile printf "$imgfile" @@ -185,10 +187,10 @@ then volfs=${volfs:1} fi -if [ ! -d /$volfs ] +if [ ! -d /$volfs ] then zfs create -p $volfs - if [ $? -gt 0 ] + if [ $? -gt 0 ] then printf "Failed to create user fs $volfs\n" >&2 exit 1 @@ -204,7 +206,7 @@ fi volimg2=$(uncompress $volimg) volimg2=$(untar $volimg2 /$volfs vmi-root) -if [ ! -f $volimg2 ] +if [ ! -f $volimg2 ] then rollback_if_needed $volfs 2 "root disk file $volimg doesn't exist\n" exit 3 @@ -218,15 +220,15 @@ fi #determine source file size -- it needs to be less than or equal to volsize imgsize=$(ls -lh $volimg2| awk -F" " '{print $5}') -if [ ${imgsize:(-1)} == G ] +if [ ${imgsize:(-1)} == G ] then - imgsize=${imgsize%G} #strip out the G + imgsize=${imgsize%G} #strip out the G imgsize=${imgsize%.*} #...and any decimal part let imgsize=imgsize+1 # add 1 to compensate for decimal part volsizetmp=${volsize%G} if [ $volsizetmp -lt $imgsize ] then - volsize=${imgsize}G + volsize=${imgsize}G fi fi @@ -235,11 +237,11 @@ tgtfile=${volfs}/vmi-root-${volname} create_from_file $volfs $volimg2 $tgtfile $volsize $cleanup volswap=$(ls -lh /$volfs | grep swap) -if [ $? -eq 0 ] +if [ $? -eq 0 ] then swapsize=$(echo $volswap | awk '{print $5}') volswap=$(echo $volswap | awk '{print $NF}') - volswap=/${volfs}/${volswap} + volswap=/${volfs}/${volswap} tgtfile=${volfs}/vmi-swap-${volname} create_from_file $volfs $volswap $tgtfile $swapsize $cleanup fi @@ -271,9 +273,4 @@ echo "volume.size=$volsize" >> /$volfs/volume.properties zfs snapshot -r $volfs@vmops_ss rollback_if_needed $volfs $? "Failed to snapshot filesystem" -#if [ "$cleanup" == "true" ] -#then - #rm -f $volimg -#fi - exit 0 diff --git a/scripts/installer/export-templates.sh b/scripts/installer/export-templates.sh old mode 100644 new mode 100755 diff --git a/scripts/installer/installcentos.sh b/scripts/installer/installcentos.sh index e68e0577a8e3..b16fadce0489 100755 --- a/scripts/installer/installcentos.sh +++ b/scripts/installer/installcentos.sh @@ -6,9 +6,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 @@ -38,7 +38,7 @@ do ;; c) cflag=1 create_tmplt_path="$OPTARG" - ;; + ;; ?) usage exit 2 ;; @@ -47,14 +47,14 @@ done shift $(($OPTIND - 1)) -if [ "$tflag" != "1" ] || [ "$cflag" != "1" ] +if [ "$tflag" != "1" ] || [ "$cflag" != "1" ] then usage exit 2 fi tmpltfs=$template_location/public/os/centos53-x86_64 -if [ "$fflag" == "1" ] +if [ "$fflag" == "1" ] then zfs destroy -Rr $tmpltfs 2> /dev/null fi diff --git a/scripts/installer/installdomp.sh b/scripts/installer/installdomp.sh index 79ea9fda45c1..9c7ab72139e5 100755 --- a/scripts/installer/installdomp.sh +++ b/scripts/installer/installdomp.sh @@ -6,9 +6,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 @@ -47,7 +47,7 @@ then fi tmpltfs=$1/private/u000000/os/consoleproxy -if [ "$fflag" == "1" ] +if [ "$fflag" == "1" ] then zfs destroy -r $tmpltfs 2> /dev/null fi diff --git a/scripts/installer/pre-check.sh b/scripts/installer/pre-check.sh new file mode 100755 index 000000000000..44c1e07a068b --- /dev/null +++ b/scripts/installer/pre-check.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +if [ ! -f "/usr/bin/java" ];then + echo "ERROR: /usr/bin/java does not exist" + exit 1 +fi + +JAVA_MAJOR_VERSION=$(/usr/bin/java --version | grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)?' | head -n 1 |cut -d "." -f1) + +if [ -z $JAVA_MAJOR_VERSION ];then + echo "WARNING: Cannot determine the JAVA version" + exit 0 +fi + +if [ "$JAVA_MAJOR_VERSION" != "21" ] && [ "$JAVA_MAJOR_VERSION" != "17" ] && [ "$JAVA_MAJOR_VERSION" != "11" ];then + echo "ERROR: JAVA $JAVA_MAJOR_VERSION is not supported. Currently only JAVA versions 11, 17 and 21 are supported." + exit 1 +fi + +if [ "$JAVA_MAJOR_VERSION" != "17" ];then + echo "WARNING: JAVA version is $JAVA_MAJOR_VERSION. JAVA 17 is recommended." +fi diff --git a/scripts/installer/run_installer.sh b/scripts/installer/run_installer.sh index 8452e3773cb1..3a96c7975403 100755 --- a/scripts/installer/run_installer.sh +++ b/scripts/installer/run_installer.sh @@ -6,9 +6,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 @@ -32,7 +32,7 @@ then fi if [ "$VMOPS_HOME" == "" ] -then +then VMOPS_HOME="/usr/local/vmops" fi diff --git a/scripts/network/domr/router_proxy.sh b/scripts/network/domr/router_proxy.sh index 945ca968d260..550ec6de91d0 100755 --- a/scripts/network/domr/router_proxy.sh +++ b/scripts/network/domr/router_proxy.sh @@ -6,9 +6,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 @@ -16,7 +16,7 @@ # specific language governing permissions and limitations # under the License. -# used as a proxy to call script inside virtual router +# used as a proxy to call script inside virtual router #set -x diff --git a/scripts/network/exdhcp/dnsmasq_edithosts.sh b/scripts/network/exdhcp/dnsmasq_edithosts.sh index 7990356edc48..f0744a9a822b 100755 --- a/scripts/network/exdhcp/dnsmasq_edithosts.sh +++ b/scripts/network/exdhcp/dnsmasq_edithosts.sh @@ -6,9 +6,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 @@ -41,9 +41,9 @@ no_dhcp_release=$? [ ! -f /etc/dhcphosts.txt ] && touch /etc/dhcphosts.txt [ ! -f /var/lib/misc/dnsmasq.leases ] && touch /var/lib/misc/dnsmasq.leases -sed -i /$1/d /etc/dhcphosts.txt -sed -i /$2,/d /etc/dhcphosts.txt -sed -i /$3,/d /etc/dhcphosts.txt +sed -i /$1/d /etc/dhcphosts.txt +sed -i /$2,/d /etc/dhcphosts.txt +sed -i /$3,/d /etc/dhcphosts.txt echo "$1,$2,$3,infinite" >>/etc/dhcphosts.txt @@ -54,9 +54,9 @@ then fi #delete leases to supplied mac and ip addresses -sed -i /$1/d /var/lib/misc/dnsmasq.leases -sed -i /"$2 "/d /var/lib/misc/dnsmasq.leases -sed -i /"$3 "/d /var/lib/misc/dnsmasq.leases +sed -i /$1/d /var/lib/misc/dnsmasq.leases +sed -i /"$2 "/d /var/lib/misc/dnsmasq.leases +sed -i /"$3 "/d /var/lib/misc/dnsmasq.leases #put in the new entry echo "0 $1 $2 $3 *" >> /var/lib/misc/dnsmasq.leases diff --git a/scripts/network/exdhcp/prepare_dhcpd.sh b/scripts/network/exdhcp/prepare_dhcpd.sh index e49d0fa175bd..657fdfead20f 100755 --- a/scripts/network/exdhcp/prepare_dhcpd.sh +++ b/scripts/network/exdhcp/prepare_dhcpd.sh @@ -6,9 +6,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/scripts/network/exdhcp/prepare_dnsmasq.sh b/scripts/network/exdhcp/prepare_dnsmasq.sh index 70aa587e60f2..5c496345e2c6 100755 --- a/scripts/network/exdhcp/prepare_dnsmasq.sh +++ b/scripts/network/exdhcp/prepare_dnsmasq.sh @@ -6,9 +6,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 @@ -129,11 +129,11 @@ config_dnsmasq domain=cloudnine.internal config_dnsmasq " # Send options to hosts which ask for a DHCP lease. # See RFC 2132 for details of available options. -# Common options can be given to dnsmasq by name: +# Common options can be given to dnsmasq by name: # run \"dnsmasq --help dhcp\" to get a list. # Note that all the common settings, such as netmask and # broadcast address, DNS server and default route, are given -# sane defaults by dnsmasq. You very likely will not need +# sane defaults by dnsmasq. You very likely will not need # any dhcp-options. If you use Windows clients and Samba, there # are some options which are recommended, they are detailed at the # end of this section. diff --git a/scripts/network/juniper/application-add.xml b/scripts/network/juniper/application-add.xml index 177329a03595..72fca676ce2b 100644 --- a/scripts/network/juniper/application-add.xml +++ b/scripts/network/juniper/application-add.xml @@ -20,11 +20,11 @@ under the License. - -%name% -%protocol% + +%name% +%protocol% %dest-port-icmp% - + diff --git a/scripts/network/juniper/application-getone.xml b/scripts/network/juniper/application-getone.xml index 4f6b5546a5cf..69e5417a58bf 100644 --- a/scripts/network/juniper/application-getone.xml +++ b/scripts/network/juniper/application-getone.xml @@ -20,9 +20,9 @@ under the License. - -%name% - + +%name% + diff --git a/scripts/network/juniper/dest-nat-rule-add.xml b/scripts/network/juniper/dest-nat-rule-add.xml index 2ef1df29accd..2f043d614f76 100644 --- a/scripts/network/juniper/dest-nat-rule-add.xml +++ b/scripts/network/juniper/dest-nat-rule-add.xml @@ -49,8 +49,4 @@ under the License. - - - - - + diff --git a/scripts/network/juniper/guest-vlan-filter-term-add.xml b/scripts/network/juniper/guest-vlan-filter-term-add.xml index d0f52a0be44d..9d4150958ccc 100644 --- a/scripts/network/juniper/guest-vlan-filter-term-add.xml +++ b/scripts/network/juniper/guest-vlan-filter-term-add.xml @@ -19,17 +19,17 @@ under the License. - - -%filter-name% - -%term-name% - -%term-name% - - - - + + +%filter-name% + +%term-name% + +%term-name% + + + + diff --git a/scripts/network/juniper/private-interface-add.xml b/scripts/network/juniper/private-interface-add.xml index f0ccb6e0e768..d291597a57af 100644 --- a/scripts/network/juniper/private-interface-add.xml +++ b/scripts/network/juniper/private-interface-add.xml @@ -20,21 +20,21 @@ under the License. - -%private-interface-name% - - -%vlan-id% -%vlan-id% - - -
-%private-interface-ip% -
-
-
-
-
+ +%private-interface-name% + + +%vlan-id% +%vlan-id% + + +
+%private-interface-ip% +
+
+
+
+
diff --git a/scripts/network/juniper/private-interface-getone.xml b/scripts/network/juniper/private-interface-getone.xml index 474e67193663..bdbb19b412c1 100644 --- a/scripts/network/juniper/private-interface-getone.xml +++ b/scripts/network/juniper/private-interface-getone.xml @@ -20,13 +20,13 @@ under the License. - -%private-interface-name% - - -%vlan-id% - - + +%private-interface-name% + + +%vlan-id% + + diff --git a/scripts/network/juniper/private-interface-with-filters-add.xml b/scripts/network/juniper/private-interface-with-filters-add.xml index 3ce8c55b242b..ea6e01f6fb16 100644 --- a/scripts/network/juniper/private-interface-with-filters-add.xml +++ b/scripts/network/juniper/private-interface-with-filters-add.xml @@ -20,14 +20,14 @@ under the License. - -%private-interface-name% - - -%vlan-id% -%vlan-id% - - + +%private-interface-name% + + +%vlan-id% +%vlan-id% + + %input-filter-name% @@ -35,14 +35,14 @@ under the License. %output-filter-name% - -
-%private-interface-ip% -
-
-
-
-
+ +
+%private-interface-ip% +
+
+
+
+
diff --git a/scripts/network/juniper/proxy-arp-add.xml b/scripts/network/juniper/proxy-arp-add.xml index ae6dee0f239c..118311fb6204 100644 --- a/scripts/network/juniper/proxy-arp-add.xml +++ b/scripts/network/juniper/proxy-arp-add.xml @@ -19,17 +19,17 @@ under the License. - - - - -%public-interface-name% -
-%public-ip-address% -
-
-
-
+ + + + +%public-interface-name% +
+%public-ip-address% +
+
+
+
diff --git a/scripts/network/juniper/proxy-arp-getall.xml b/scripts/network/juniper/proxy-arp-getall.xml index 3f23a22cd5a3..3ec425c28fc1 100644 --- a/scripts/network/juniper/proxy-arp-getall.xml +++ b/scripts/network/juniper/proxy-arp-getall.xml @@ -19,12 +19,12 @@ under the License. - - - -%interface-name% - - + + + +%interface-name% + + diff --git a/scripts/network/juniper/proxy-arp-getone.xml b/scripts/network/juniper/proxy-arp-getone.xml index e43dc0bc5ea7..3299dac9d0e1 100644 --- a/scripts/network/juniper/proxy-arp-getone.xml +++ b/scripts/network/juniper/proxy-arp-getone.xml @@ -19,17 +19,17 @@ under the License. - - - - -%public-interface-name% -
-%public-ip-address% -
-
-
-
+ + + + +%public-interface-name% +
+%public-ip-address% +
+
+
+
diff --git a/scripts/network/juniper/public-ip-filter-term-add.xml b/scripts/network/juniper/public-ip-filter-term-add.xml index 9aad4c239902..e8ffd7aad41a 100644 --- a/scripts/network/juniper/public-ip-filter-term-add.xml +++ b/scripts/network/juniper/public-ip-filter-term-add.xml @@ -19,22 +19,22 @@ under the License. - - -%filter-name% - -%term-name% - -<%address-type%> -%ip-address% - - - -%term-name% - - - - + + +%filter-name% + +%term-name% + +<%address-type%> +%ip-address% + + + +%term-name% + + + + diff --git a/scripts/network/juniper/security-policy-group.xml b/scripts/network/juniper/security-policy-group.xml index 1d0dc8cba123..46b57b0bfcbc 100644 --- a/scripts/network/juniper/security-policy-group.xml +++ b/scripts/network/juniper/security-policy-group.xml @@ -19,13 +19,13 @@ under the License. - - - -%from-zone% -%to-zone% - - + + + +%from-zone% +%to-zone% + + diff --git a/scripts/network/juniper/zone-interface-add.xml b/scripts/network/juniper/zone-interface-add.xml index 9b2d37278ac9..5f6d4c0416a6 100644 --- a/scripts/network/juniper/zone-interface-add.xml +++ b/scripts/network/juniper/zone-interface-add.xml @@ -19,15 +19,15 @@ under the License. - - - -%private-zone-name% - -%zone-interface-name% - - - + + + +%private-zone-name% + +%zone-interface-name% + + + diff --git a/scripts/network/juniper/zone-interface-getone.xml b/scripts/network/juniper/zone-interface-getone.xml index 4bc5c4bc0d28..2078c015ba42 100644 --- a/scripts/network/juniper/zone-interface-getone.xml +++ b/scripts/network/juniper/zone-interface-getone.xml @@ -19,15 +19,15 @@ under the License. - - - -%private-zone-name% - -%zone-interface-name% - - - + + + +%private-zone-name% + +%zone-interface-name% + + + diff --git a/scripts/network/ping/baremetal_user_data.py b/scripts/network/ping/baremetal_user_data.py index 5a1893534675..02fe845392c9 100755 --- a/scripts/network/ping/baremetal_user_data.py +++ b/scripts/network/ping/baremetal_user_data.py @@ -38,7 +38,7 @@ def writeIfNotHere(fileName, texts): texts = [ "%s\n" % t for t in texts ] need = False for t in texts: - if not t in entries: + if t not in entries: entries.append(t) need = True diff --git a/scripts/storage/checkchildren.sh b/scripts/storage/checkchildren.sh index 55122d4f3a04..4185a7c25dff 100755 --- a/scripts/storage/checkchildren.sh +++ b/scripts/storage/checkchildren.sh @@ -6,9 +6,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/scripts/storage/installIso.sh b/scripts/storage/installIso.sh index fd301bc5ca14..89744ac3825a 100755 --- a/scripts/storage/installIso.sh +++ b/scripts/storage/installIso.sh @@ -6,9 +6,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 @@ -29,7 +29,7 @@ usage() { verify_cksum() { echo "$1 $2" | md5sum -c --status - if [ $? -gt 0 ] + if [ $? -gt 0 ] then printf "Checksum failed, not proceeding with install\n" exit 3 @@ -44,7 +44,7 @@ install_file() { mv $isofile /$isofs/$tmpltname - if [ $? -gt 0 ] + if [ $? -gt 0 ] then printf "Move operation failed, iso $isofile not installed\n" exit 4 @@ -55,7 +55,7 @@ install_file() { isofs=$isofs/$file mp=${isofs%/iso/*} mp=/$mp/iso - path=${isofs:${#mp}} + path=${isofs:${#mp}} pushd $mp ln -s $path $file popd @@ -108,10 +108,10 @@ then isofs=${isofs:1} fi -if [ ! -d /$isofs ] +if [ ! -d /$isofs ] then mkdir -p /$isofs - if [ $? -gt 0 ] + if [ $? -gt 0 ] then printf "Failed to create iso fs $isofs\n" >&2 exit 1 diff --git a/scripts/storage/multipath/cleanStaleMaps.sh b/scripts/storage/multipath/cleanStaleMaps.sh index 90b9bef5a8de..c1ded42943c6 100755 --- a/scripts/storage/multipath/cleanStaleMaps.sh +++ b/scripts/storage/multipath/cleanStaleMaps.sh @@ -22,10 +22,18 @@ # ############################################################################################# +SCRIPT_NAME=$(basename "$0") + +if [[ $(pgrep -f ${SCRIPT_NAME}) != "$$" ]]; then + echo "Another instance of ${SCRIPT_NAME} is already running! Exiting" + exit +fi + + cd $(dirname $0) for WWID in $(multipathd list maps status | awk '{ if ($4 == 0) { print substr($1,2); }}'); do - ./removeVolume.sh ${WWID} + ./disconnectVolume.sh ${WWID} done exit 0 diff --git a/scripts/storage/multipath/connectVolume.sh b/scripts/storage/multipath/connectVolume.sh index fb8387ece473..1e2052694cbf 100755 --- a/scripts/storage/multipath/connectVolume.sh +++ b/scripts/storage/multipath/connectVolume.sh @@ -100,7 +100,7 @@ while true; do done echo "$(date): Doing a recan to make sure we have proper current size locally" -for device in $(multipath -ll 3${WWID} | egrep '^ ' | awk '{print $2}'); do +for device in $(multipath -ll 3${WWID} | grep -E '^ ' | awk '{print $2}'); do echo "1" > /sys/bus/scsi/drivers/sd/${device}/rescan; done diff --git a/scripts/storage/multipath/copyVolume.sh b/scripts/storage/multipath/copyVolume.sh index d169198251be..8e6609ea1083 100755 --- a/scripts/storage/multipath/copyVolume.sh +++ b/scripts/storage/multipath/copyVolume.sh @@ -22,7 +22,7 @@ OUTPUT_FILE=${3:?"Output file/path is required"} echo "$(date): qemu-img convert -n -p -W -t none -O ${OUTPUT_FORMAT} ${INPUT_FILE} ${OUTPUT_FILE}" -qemu-img convert -n -p -W -t none -O ${OUTPUT_FORMAT} ${INPUT_FILE} ${OUTPUT_FILE} && { +qemu-img convert -n -p -W -t writeback -O ${OUTPUT_FORMAT} ${INPUT_FILE} ${OUTPUT_FILE} && { # if its a block device make sure we flush caches before exiting lsblk ${OUTPUT_FILE} >/dev/null 2>&1 && { blockdev --flushbufs ${OUTPUT_FILE} diff --git a/scripts/storage/multipath/disconnectVolume.sh b/scripts/storage/multipath/disconnectVolume.sh index 067e561f8a33..f894076927f1 100755 --- a/scripts/storage/multipath/disconnectVolume.sh +++ b/scripts/storage/multipath/disconnectVolume.sh @@ -66,6 +66,9 @@ fi logger -t CS_SCSI_VOL_REMOVE "${WWID} successfully purged from multipath along with slave devices" +# Added to give time for the event to be fired to the server +sleep 10 + echo "$(date): ${WWID} removed" exit 0 diff --git a/scripts/storage/multipath/resizeVolume.sh b/scripts/storage/multipath/resizeVolume.sh index 1b44a71b46ae..491c1bd93a1c 100755 --- a/scripts/storage/multipath/resizeVolume.sh +++ b/scripts/storage/multipath/resizeVolume.sh @@ -51,7 +51,7 @@ systemctl is-active multipathd || systemctl restart multipathd || { logger -t "CS_SCSI_VOL_RESIZE" "${WWID} resizing disk path at /dev/mapper/3${WWID} STARTING" -for device in $(multipath -ll 3${WWID} | egrep '^ ' | awk '{print $2}'); do +for device in $(multipath -ll 3${WWID} | grep -E '^ ' | awk '{print $2}'); do echo "1" > /sys/bus/scsi/drivers/sd/${device}/rescan; done diff --git a/scripts/storage/qcow2/create_private_template.sh b/scripts/storage/qcow2/create_private_template.sh index 8e9e26c41040..06bf79d44cbf 100755 --- a/scripts/storage/qcow2/create_private_template.sh +++ b/scripts/storage/qcow2/create_private_template.sh @@ -6,9 +6,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 @@ -16,7 +16,7 @@ # specific language governing permissions and limitations # under the License. - + # $Id: create_private_template.sh 9804 2010-06-22 18:36:49Z alex $ $HeadURL: svn://svn.lab.vmops.com/repos/vmdev/java/scripts/storage/qcow2/create_private_template.sh $ # create_private_template.sh -- create a private template from a snapshot @@ -31,7 +31,7 @@ create_template() { local fspath=$1 local destpath=$2 - # if backing image exists, we need to combine them, otherwise + # if backing image exists, we need to combine them, otherwise # copy the image to preserve snapshots/compression if $qemu_img info "$tmpltimg" | grep -q backing; then qemu-img convert -O qcow2 /$fspath $destpath diff --git a/scripts/storage/qcow2/createtmplt.sh b/scripts/storage/qcow2/createtmplt.sh index b05550c14bbd..de7010b0a25e 100755 --- a/scripts/storage/qcow2/createtmplt.sh +++ b/scripts/storage/qcow2/createtmplt.sh @@ -6,9 +6,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 @@ -65,17 +65,19 @@ uncompress() { ;; [zZ][iI][pP]) unzip -p $1 | cat > $tmpfile ;; + XZ) xz -d -c $1 > $tmpfile + ;; *) printf "$1" return 0 ;; esac - if [ $? -gt 0 ] + if [ $? -gt 0 ] then printf "Failed to uncompress file, exiting " - exit 1 + exit 1 fi - + mv $tmpfile $imgfile printf "$imgfile" @@ -89,7 +91,7 @@ create_from_file() { if [ -b $tmpltimg ]; then $qemu_img convert -f raw -O qcow2 "$tmpltimg" /$tmpltfs/$tmpltname else - # if backing image exists, we need to combine them, otherwise + # if backing image exists, we need to combine them, otherwise # copy the image to preserve snapshots/compression if $qemu_img info "$tmpltimg" | grep -q backing; then $qemu_img convert -f qcow2 -O qcow2 "$tmpltimg" /$tmpltfs/$tmpltname >& /dev/null @@ -97,7 +99,7 @@ create_from_file() { cp -f $tmpltimg /$tmpltfs/$tmpltname fi fi - + if [ "$cleanup" == "true" ] then rm -f "$tmpltimg" @@ -161,17 +163,17 @@ do done -if [ ! -d /$tmpltfs ] +if [ ! -d /$tmpltfs ] then mkdir -p /$tmpltfs - if [ $? -gt 0 ] + if [ $? -gt 0 ] then printf "Failed to create user fs $tmpltfs\n" >&2 exit 1 fi fi -if [ ! -f $tmpltimg -a ! -b $tmpltimg ] +if [ ! -f $tmpltimg -a ! -b $tmpltimg ] then printf "root disk file $tmpltimg doesn't exist\n" exit 3 diff --git a/scripts/storage/qcow2/createvm.sh b/scripts/storage/qcow2/createvm.sh index 0971c9b92553..e3a83aed3bf7 100755 --- a/scripts/storage/qcow2/createvm.sh +++ b/scripts/storage/qcow2/createvm.sh @@ -6,9 +6,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 @@ -18,11 +18,11 @@ # $Id: createvm.sh 10292 2010-07-07 00:24:04Z edison $ $HeadURL: svn://svn.lab.vmops.com/repos/vmdev/java/scripts/storage/qcow2/createvm.sh $ -# createvm.sh -- create a vm image +# createvm.sh -- create a vm image usage() { echo "Usage (clone VM from template): createvm.sh -t diff --git a/ui/src/components/view/ObjectStoreBrowser.vue b/ui/src/components/view/ObjectStoreBrowser.vue index 531846a9da57..5507540d1f41 100644 --- a/ui/src/components/view/ObjectStoreBrowser.vue +++ b/ui/src/components/view/ObjectStoreBrowser.vue @@ -468,7 +468,8 @@ export default { return false }, uploadFiles () { - if (!this.uploadDirectory.endsWith('/')) { + this.uploadDirectory = this.uploadDirectory.trim() + if (this.uploadDirectory.length !== 0 && !this.uploadDirectory.endsWith('/')) { this.uploadDirectory = this.uploadDirectory + '/' } var promises = [] @@ -488,7 +489,12 @@ export default { asyncUploadFile (file, objectName) { return new Promise((resolve, reject) => { file.arrayBuffer().then((buffer) => { - this.client.putObject(this.resource.name, objectName, Buffer.from(buffer), file.size, this.uploadMetaData, err => { + const metadata = { + ...this.uploadMetaData, + 'Content-Type': file.type || 'binary/octet-stream' + } + + this.client.putObject(this.resource.name, objectName, Buffer.from(buffer), file.size, metadata, err => { if (err) { return reject(this.$notification.error({ message: this.$t('message.upload.failed'), diff --git a/ui/src/components/view/ResourceCountUsage.vue b/ui/src/components/view/ResourceCountUsage.vue index ecd24a18291a..c74391f3a9bc 100644 --- a/ui/src/components/view/ResourceCountUsage.vue +++ b/ui/src/components/view/ResourceCountUsage.vue @@ -36,6 +36,37 @@ :percent="parseFloat(getPercentUsed(resource[item + 'total'], resource[item + 'limit']))" :format="p => resource[item + 'limit'] !== '-1' && resource[item + 'limit'] !== 'Unlimited' ? p.toFixed(2) + '%' : ''" /> + + + + + + + @@ -43,6 +74,8 @@ diff --git a/ui/src/components/view/SearchView.vue b/ui/src/components/view/SearchView.vue index 87596da66cc5..bd952f049476 100644 --- a/ui/src/components/view/SearchView.vue +++ b/ui/src/components/view/SearchView.vue @@ -66,12 +66,13 @@ return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 }" :loading="field.loading" - @input="onchange($event, field.name)"> + @input="onchange($event, field.name)" + @change="onSelectFieldChange(field.name)"> + :value="['account'].includes(field.name) ? opt.name : opt.id" + :label="field.name === 'vgpuprofileid' ? `${opt.gpucardname} - ${opt.name}` : $t((field.name.startsWith('domain') && opt.path) ? opt.path : opt.name)">
@@ -79,13 +80,21 @@ - + - {{ $t(opt.path || opt.name) }} + + + + + {{ opt.gpucardname }} - {{ opt.name }} + + + {{ $t((field.name.startsWith('domain') && opt.path) ? opt.path : opt.name) }} +
@@ -107,6 +116,10 @@ + import { ref, reactive, toRaw } from 'vue' -import { api } from '@/api' +import { getAPI } from '@/api' +import { isAdmin } from '@/role' import TooltipButton from '@/components/widgets/TooltipButton' import ResourceIcon from '@/components/view/ResourceIcon' +import Status from '@/components/widgets/Status' +import { i18n } from '@/locales' export default { name: 'SearchView', components: { TooltipButton, - ResourceIcon + ResourceIcon, + Status }, props: { searchFilters: { @@ -186,7 +203,8 @@ export default { inputKey: null, inputValue: null, fieldValues: {}, - isFiltered: false + isFiltered: false, + alertTypes: [] } }, created () { @@ -205,13 +223,7 @@ export default { if (to && to.query && 'q' in to.query) { this.searchQuery = to.query.q } - this.isFiltered = false - this.searchFilters.some(item => { - if (this.searchParams[item]) { - this.isFiltered = true - return true - } - }) + this.updateIsFiltered() } }, mounted () { @@ -219,6 +231,7 @@ export default { if (this.$route && this.$route.query && 'q' in this.$route.query) { this.searchQuery = this.$route.query.q } + this.updateIsFiltered() }, computed: { styleSearch () { @@ -240,6 +253,19 @@ export default { onchange: async function (event, fieldname) { this.fetchDynamicFieldData(fieldname, event.target.value) }, + onSelectFieldChange (fieldname) { + const fetchAccountOptions = fieldname === 'domainid' && this.fields.some((field) => field.name === 'account') + if (fetchAccountOptions) { + this.fetchDynamicFieldData('account') + } + + const fetchVgpuProfileOptions = fieldname === 'gpucardid' && this.fields.some((field) => field.name === 'vgpuprofileid') + if (fetchVgpuProfileOptions) { + // Clear the currently selected vGPU profile when GPU card changes + this.form.vgpuprofileid = null + this.fetchDynamicFieldData('vgpuprofileid') + } + }, onVisibleForm () { this.visibleFilter = !this.visibleFilter if (!this.visibleFilter) return @@ -267,6 +293,9 @@ export default { if (item === 'domainid' && !('listDomains' in this.$store.getters.apis)) { return true } + if (item === 'account' && !('listAccounts' in this.$store.getters.apis)) { + return true + } if (item === 'account' && !('addAccountToProject' in this.$store.getters.apis || 'createAccount' in this.$store.getters.apis)) { return true } @@ -279,12 +308,32 @@ export default { if (item === 'groupid' && !('listInstanceGroups' in this.$store.getters.apis)) { return true } - if (['zoneid', 'domainid', 'imagestoreid', 'storageid', 'state', 'level', 'clusterid', 'podid', 'groupid', 'entitytype', 'type'].includes(item)) { + if (item === 'associatednetworkid' && this.$route.meta.name === 'asnumbers') { + item = 'networkid' + } + if (item === 'usagetype' && !('listUsageTypes' in this.$store.getters.apis)) { + return true + } + if (['isencrypted', 'volumeid'].includes(item) && !('listVolumes' in this.$store.getters.apis)) { + return true + } + if (item === 'backupofferingid' && !('listBackupOfferings' in this.$store.getters.apis)) { + return true + } + if (['zoneid', 'domainid', 'imagestoreid', 'storageid', 'state', 'account', 'hypervisor', 'level', + 'clusterid', 'podid', 'groupid', 'entitytype', 'accounttype', 'systemvmtype', 'scope', 'provider', + 'type', 'scope', 'managementserverid', 'serviceofferingid', + 'diskofferingid', 'networkid', 'usagetype', 'restartrequired', 'gpuenabled', + 'displaynetwork', 'guestiptype', 'usersource', 'arch', 'oscategoryid', 'templatetype', 'gpucardid', 'vgpuprofileid', + 'extensionid', 'backupoffering', 'volumeid', 'virtualmachineid'].includes(item) + ) { type = 'list' } else if (item === 'tags') { type = 'tag' - } else if (item === 'resourcetype') { + } else if (['resourcetype', 'apikeyaccess'].includes(item)) { type = 'autocomplete' + } else if (item === 'isencrypted') { + type = 'boolean' } this.fields.push({ @@ -298,15 +347,33 @@ export default { return arrayField }, fetchStaticFieldData (arrayField) { - if (arrayField.includes('type')) { - if (this.$route.path === '/guestnetwork' || this.$route.path.includes('/guestnetwork/')) { - const typeIndex = this.fields.findIndex(item => item.name === 'type') + if (arrayField.includes('displaynetwork')) { + const typeIndex = this.fields.findIndex(item => item.name === 'displaynetwork') + this.fields[typeIndex].loading = true + this.fields[typeIndex].opts = this.fetchBoolean() + this.fields[typeIndex].loading = false + } + if (arrayField.includes('type') || arrayField.includes('guestiptype')) { + if (this.$route.path.includes('/guestnetwork') || this.$route.path.includes('/networkoffering')) { + const typeIndex = this.fields.findIndex(item => ['type', 'guestiptype'].includes(item.name)) this.fields[typeIndex].loading = true this.fields[typeIndex].opts = this.fetchGuestNetworkTypes() this.fields[typeIndex].loading = false + } else if (this.$route.path === '/role' || this.$route.path.includes('/role/')) { + const typeIndex = this.fields.findIndex(item => item.name === 'type') + this.fields[typeIndex].loading = true + this.fields[typeIndex].opts = this.fetchRoleTypes() + this.fields[typeIndex].loading = false } } + if (arrayField.includes('scope')) { + const scopeIndex = this.fields.findIndex(item => item.name === 'scope') + this.fields[scopeIndex].loading = true + this.fields[scopeIndex].opts = this.fetchScope() + this.fields[scopeIndex].loading = false + } + if (arrayField.includes('state')) { const stateIndex = this.fields.findIndex(item => item.name === 'state') this.fields[stateIndex].loading = true @@ -328,6 +395,54 @@ export default { this.fields[entityTypeIndex].loading = false } + if (arrayField.includes('accounttype')) { + const accountTypeIndex = this.fields.findIndex(item => item.name === 'accounttype') + this.fields[accountTypeIndex].loading = true + this.fields[accountTypeIndex].opts = this.fetchAccountTypes() + this.fields[accountTypeIndex].loading = false + } + + if (arrayField.includes('systemvmtype')) { + const systemVmTypeIndex = this.fields.findIndex(item => item.name === 'systemvmtype') + this.fields[systemVmTypeIndex].loading = true + this.fields[systemVmTypeIndex].opts = this.fetchSystemVmTypes() + this.fields[systemVmTypeIndex].loading = false + } + + if (arrayField.includes('scope')) { + const scopeIndex = this.fields.findIndex(item => item.name === 'scope') + this.fields[scopeIndex].loading = true + this.fields[scopeIndex].opts = this.fetchStoragePoolScope() + this.fields[scopeIndex].loading = false + } + + if (arrayField.includes('provider')) { + const providerIndex = this.fields.findIndex(item => item.name === 'provider') + this.fields[providerIndex].loading = true + this.fields[providerIndex].opts = this.fetchImageStoreProviders() + this.fields[providerIndex].loading = false + } + + if (arrayField.includes('restartrequired')) { + const restartRequiredIndex = this.fields.findIndex(item => item.name === 'restartrequired') + this.fields[restartRequiredIndex].loading = true + this.fields[restartRequiredIndex].opts = [ + { id: 'true', name: 'label.yes' }, + { id: 'false', name: 'label.no' } + ] + this.fields[restartRequiredIndex].loading = false + } + + if (arrayField.includes('gpuenabled')) { + const gpuEnabledIndex = this.fields.findIndex(item => item.name === 'gpuenabled') + this.fields[gpuEnabledIndex].loading = true + this.fields[gpuEnabledIndex].opts = [ + { id: 'true', name: 'label.yes' }, + { id: 'false', name: 'label.no' } + ] + this.fields[gpuEnabledIndex].loading = false + } + if (arrayField.includes('resourcetype')) { const resourceTypeIndex = this.fields.findIndex(item => item.name === 'resourcetype') this.fields[resourceTypeIndex].loading = true @@ -339,20 +454,80 @@ export default { { value: 'Template' }, { value: 'User' }, { value: 'VirtualMachine' }, - { value: 'Volume' } + { value: 'Volume' }, + { value: 'QuotaTariff' } ] this.fields[resourceTypeIndex].loading = false } + + if (arrayField.includes('apikeyaccess')) { + const apiKeyAccessIndex = this.fields.findIndex(item => item.name === 'apikeyaccess') + this.fields[apiKeyAccessIndex].loading = true + this.fields[apiKeyAccessIndex].opts = [ + { value: 'Disabled' }, + { value: 'Enabled' }, + { value: 'Inherit' } + ] + this.fields[apiKeyAccessIndex].loading = false + } + + if (arrayField.includes('usersource')) { + const userSourceIndex = this.fields.findIndex(item => item.name === 'usersource') + this.fields[userSourceIndex].loading = true + this.fields[userSourceIndex].opts = this.fetchAvailableUserSourceTypes() + this.fields[userSourceIndex].loading = false + } + + if (arrayField.includes('arch')) { + const typeIndex = this.fields.findIndex(item => item.name === 'arch') + this.fields[typeIndex].loading = true + this.fields[typeIndex].opts = this.$fetchCpuArchitectureTypes() + this.fields[typeIndex].loading = false + } + + if (arrayField.includes('templatetype')) { + const typeIndex = this.fields.findIndex(item => item.name === 'templatetype') + this.fields[typeIndex].loading = true + this.fields[typeIndex].opts = this.$fetchTemplateTypes() + this.fields[typeIndex].loading = false + } }, async fetchDynamicFieldData (arrayField, searchKeyword) { const promises = [] + let typeIndex = -1 let zoneIndex = -1 let domainIndex = -1 + let accountIndex = -1 + let hypervisorIndex = -1 let imageStoreIndex = -1 let storageIndex = -1 let podIndex = -1 let clusterIndex = -1 let groupIndex = -1 + let managementServerIdIndex = -1 + let serviceOfferingIndex = -1 + let diskOfferingIndex = -1 + let networkIndex = -1 + let usageTypeIndex = -1 + let volumeIndex = -1 + let virtualmachineIndex = -1 + let backupOfferingIndex = -1 + let osCategoryIndex = -1 + let gpuCardIndex = -1 + let vgpuProfileIndex = -1 + let extensionIndex = -1 + + if (arrayField.includes('type')) { + if (this.$route.path === '/alert') { + typeIndex = this.fields.findIndex(item => item.name === 'type') + this.fields[typeIndex].loading = true + promises.push(await this.fetchAlertTypes()) + } else if (this.$route.path === '/affinitygroup') { + typeIndex = this.fields.findIndex(item => item.name === 'type') + this.fields[typeIndex].loading = true + promises.push(await this.fetchAffinityGroupTypes()) + } + } if (arrayField.includes('zoneid')) { zoneIndex = this.fields.findIndex(item => item.name === 'zoneid') @@ -360,12 +535,30 @@ export default { promises.push(await this.fetchZones(searchKeyword)) } + if (arrayField.includes('extensionid')) { + extensionIndex = this.fields.findIndex(item => item.name === 'extensionid') + this.fields[extensionIndex].loading = true + promises.push(await this.fetchExtensions(searchKeyword)) + } + if (arrayField.includes('domainid')) { domainIndex = this.fields.findIndex(item => item.name === 'domainid') this.fields[domainIndex].loading = true promises.push(await this.fetchDomains(searchKeyword)) } + if (arrayField.includes('account')) { + accountIndex = this.fields.findIndex(item => item.name === 'account') + this.fields[accountIndex].loading = true + promises.push(await this.fetchAccounts(searchKeyword)) + } + + if (arrayField.includes('hypervisor')) { + hypervisorIndex = this.fields.findIndex(item => item.name === 'hypervisor') + this.fields[hypervisorIndex].loading = true + promises.push(await this.fetchHypervisors()) + } + if (arrayField.includes('imagestoreid')) { imageStoreIndex = this.fields.findIndex(item => item.name === 'imagestoreid') this.fields[imageStoreIndex].loading = true @@ -396,19 +589,115 @@ export default { promises.push(await this.fetchInstanceGroups(searchKeyword)) } + if (arrayField.includes('virtualmachineid')) { + virtualmachineIndex = this.fields.findIndex(item => item.name === 'virtualmachineid') + this.fields[virtualmachineIndex].loading = true + promises.push(await this.fetchVirtualMachines(searchKeyword)) + } + + if (arrayField.includes('managementserverid')) { + managementServerIdIndex = this.fields.findIndex(item => item.name === 'managementserverid') + this.fields[managementServerIdIndex].loading = true + promises.push(await this.fetchManagementServers(searchKeyword)) + } + + if (arrayField.includes('serviceofferingid')) { + serviceOfferingIndex = this.fields.findIndex(item => item.name === 'serviceofferingid') + this.fields[serviceOfferingIndex].loading = true + promises.push(await this.fetchServiceOfferings(searchKeyword)) + } + + if (arrayField.includes('diskofferingid')) { + diskOfferingIndex = this.fields.findIndex(item => item.name === 'diskofferingid') + this.fields[diskOfferingIndex].loading = true + promises.push(await this.fetchDiskOfferings(searchKeyword)) + } + + if (arrayField.includes('networkid')) { + networkIndex = this.fields.findIndex(item => item.name === 'networkid') + this.fields[networkIndex].loading = true + promises.push(await this.fetchNetworks(searchKeyword)) + } + + if (arrayField.includes('usagetype')) { + usageTypeIndex = this.fields.findIndex(item => item.name === 'usagetype') + this.fields[usageTypeIndex].loading = true + promises.push(await this.fetchUsageTypes()) + } + + if (arrayField.includes('isencrypted')) { + volumeIndex = this.fields.findIndex(item => item.name === 'isencrypted') + this.fields[volumeIndex].loading = true + promises.push(await this.fetchVolumes(searchKeyword)) + } + + if (arrayField.includes('oscategoryid')) { + osCategoryIndex = this.fields.findIndex(item => item.name === 'oscategoryid') + this.fields[osCategoryIndex].loading = true + promises.push(await this.fetchOsCategories(searchKeyword)) + } + + if (arrayField.includes('backupofferingid')) { + backupOfferingIndex = this.fields.findIndex(item => item.name === 'backupofferingid') + this.fields[backupOfferingIndex].loading = true + promises.push(await this.fetchBackupOfferings(searchKeyword)) + } + + if (arrayField.includes('gpucardid')) { + gpuCardIndex = this.fields.findIndex(item => item.name === 'gpucardid') + this.fields[gpuCardIndex].loading = true + promises.push(await this.fetchGpuCards(searchKeyword)) + } + + if (arrayField.includes('vgpuprofileid')) { + vgpuProfileIndex = this.fields.findIndex(item => item.name === 'vgpuprofileid') + this.fields[vgpuProfileIndex].loading = true + promises.push(await this.fetchVgpuProfiles(searchKeyword)) + } + + if (arrayField.includes('volumeid')) { + volumeIndex = this.fields.findIndex(item => item.name === 'volumeid') + this.fields[volumeIndex].loading = true + promises.push(await this.fetchVolumes(searchKeyword)) + } + Promise.all(promises).then(response => { + if (typeIndex > -1) { + const types = response.filter(item => item.type === 'type') + if (types && types.length > 0) { + this.fields[typeIndex].opts = this.sortArray(types[0].data) + } + } if (zoneIndex > -1) { const zones = response.filter(item => item.type === 'zoneid') if (zones && zones.length > 0) { this.fields[zoneIndex].opts = this.sortArray(zones[0].data) } } + if (extensionIndex > -1) { + const extensions = response.filter(item => item.type === 'extensionid') + if (extensions && extensions.length > 0) { + this.fields[extensionIndex].opts = this.sortArray(extensions[0].data) + } + } if (domainIndex > -1) { const domain = response.filter(item => item.type === 'domainid') if (domain && domain.length > 0) { this.fields[domainIndex].opts = this.sortArray(domain[0].data, 'path') } } + if (accountIndex > -1) { + const account = response.filter(item => item.type === 'account') + if (account && account.length > 0) { + this.fields[accountIndex].opts = this.sortArray(account[0].data, 'name') + } + } + if (hypervisorIndex > -1) { + const hypervisor = response.filter(item => item.type === 'hypervisor') + if (hypervisor && hypervisor.length > 0) { + this.fields[hypervisorIndex].opts = this.sortArray(hypervisor[0].data, 'name') + } + } if (imageStoreIndex > -1) { const imageStore = response.filter(item => item.type === 'imagestoreid') if (imageStore && imageStore.length > 0) { @@ -439,13 +728,102 @@ export default { this.fields[groupIndex].opts = this.sortArray(groups[0].data) } } + + if (managementServerIdIndex > -1) { + const managementServers = response.filter(item => item.type === 'managementserverid') + if (managementServers && managementServers.length > 0) { + this.fields[managementServerIdIndex].opts = this.sortArray(managementServers[0].data) + } + } + + if (serviceOfferingIndex > -1) { + const serviceOfferings = response.filter(item => item.type === 'serviceofferingid') + if (serviceOfferings && serviceOfferings.length > 0) { + this.fields[serviceOfferingIndex].opts = this.sortArray(serviceOfferings[0].data) + } + } + + if (diskOfferingIndex > -1) { + const diskOfferings = response.filter(item => item.type === 'diskofferingid') + if (diskOfferings && diskOfferings.length > 0) { + this.fields[diskOfferingIndex].opts = this.sortArray(diskOfferings[0].data) + } + } + + if (networkIndex > -1) { + const networks = response.filter(item => item.type === 'networkid') + if (networks && networks.length > 0) { + this.fields[networkIndex].opts = this.sortArray(networks[0].data) + } + } + + if (usageTypeIndex > -1) { + const usageTypes = response.filter(item => item.type === 'usagetype') + if (usageTypes?.length > 0) { + this.fields[usageTypeIndex].opts = this.sortArray(usageTypes[0].data) + } + } + + if (osCategoryIndex > -1) { + const osCategories = response.filter(item => item.type === 'oscategoryid') + if (osCategories && osCategories.length > 0) { + this.fields[osCategoryIndex].opts = this.sortArray(osCategories[0].data) + } + } + + if (backupOfferingIndex > -1) { + const backupOfferings = response.filter(item => item.type === 'backupofferingid') + if (backupOfferings?.length > 0) { + this.fields[backupOfferingIndex].opts = this.sortArray(backupOfferings[0].data) + } + } + + if (gpuCardIndex > -1) { + const gpuCards = response.filter(item => item.type === 'gpucardid') + if (gpuCards && gpuCards.length > 0) { + this.fields[gpuCardIndex].opts = this.sortArray(gpuCards[0].data) + } + } + + if (vgpuProfileIndex > -1) { + const vgpuProfiles = response.filter(item => item.type === 'vgpuprofileid') + if (vgpuProfiles && vgpuProfiles.length > 0) { + this.fields[vgpuProfileIndex].opts = this.sortArray(vgpuProfiles[0].data) + } + } + + if (volumeIndex > -1) { + const volumes = response.filter(item => ['volumeid', 'isencrypted'].includes(item.type)) + if (volumes && volumes.length > 0) { + this.fields[volumeIndex].opts = this.sortArray(volumes[0].data) + } + } + + if (virtualmachineIndex > -1) { + const virtualMachines = response.filter(item => item.type === 'virtualmachineid') + if (virtualMachines && virtualMachines.length > 0) { + this.fields[virtualmachineIndex].opts = this.sortArray(virtualMachines[0].data) + } + } }).finally(() => { + if (typeIndex > -1) { + this.fields[typeIndex].loading = false + } if (zoneIndex > -1) { this.fields[zoneIndex].loading = false } + if (extensionIndex > -1) { + this.fields[extensionIndex].loading = false + } if (domainIndex > -1) { this.fields[domainIndex].loading = false } + if (accountIndex > -1) { + this.fields[accountIndex].loading = false + } + if (hypervisorIndex > -1) { + this.fields[hypervisorIndex].loading = false + } if (imageStoreIndex > -1) { this.fields[imageStoreIndex].loading = false } @@ -461,7 +839,42 @@ export default { if (groupIndex > -1) { this.fields[groupIndex].loading = false } - this.fillFormFieldValues() + if (managementServerIdIndex > -1) { + this.fields[managementServerIdIndex].loading = false + } + if (serviceOfferingIndex > -1) { + this.fields[serviceOfferingIndex].loading = false + } + if (diskOfferingIndex > -1) { + this.fields[diskOfferingIndex].loading = false + } + if (networkIndex > -1) { + this.fields[networkIndex].loading = false + } + if (usageTypeIndex > -1) { + this.fields[usageTypeIndex].loading = false + } + if (osCategoryIndex > -1) { + this.fields[osCategoryIndex].loading = false + } + if (backupOfferingIndex > -1) { + this.fields[backupOfferingIndex].loading = false + } + if (gpuCardIndex > -1) { + this.fields[gpuCardIndex].loading = false + } + if (vgpuProfileIndex > -1) { + this.fields[vgpuProfileIndex].loading = false + } + if (volumeIndex > -1) { + this.fields[volumeIndex].loading = false + } + if (virtualmachineIndex > -1) { + this.fields[virtualmachineIndex].loading = false + } + if (Array.isArray(arrayField)) { + this.fillFormFieldValues() + } }) }, initFormFieldData () { @@ -472,6 +885,9 @@ export default { this.fetchDynamicFieldData(arrayField) }, sortArray (data, key = 'name') { + if (!data) { + return [] + } return data.sort(function (a, b) { if (a[key] < b[key]) { return -1 } if (a[key] > b[key]) { return 1 } @@ -495,7 +911,7 @@ export default { }, fetchZones (searchKeyword) { return new Promise((resolve, reject) => { - api('listZones', { showicon: true, keyword: searchKeyword }).then(json => { + getAPI('listZones', { showicon: true, keyword: searchKeyword }).then(json => { const zones = json.listzonesresponse.zone resolve({ type: 'zoneid', @@ -506,9 +922,22 @@ export default { }) }) }, + fetchExtensions (searchKeyword) { + return new Promise((resolve, reject) => { + getAPI('listExtensions', { details: 'min', showicon: true, keyword: searchKeyword }).then(json => { + const extensions = json.listextensionsresponse.extension + resolve({ + type: 'extensionid', + data: extensions + }) + }).catch(error => { + reject(error.response.headers['x-description']) + }) + }) + }, fetchDomains (searchKeyword) { return new Promise((resolve, reject) => { - api('listDomains', { listAll: true, showicon: true, keyword: searchKeyword }).then(json => { + getAPI('listDomains', { listAll: true, details: 'min', showicon: true, keyword: searchKeyword }).then(json => { const domain = json.listdomainsresponse.domain resolve({ type: 'domainid', @@ -519,9 +948,42 @@ export default { }) }) }, + fetchAccounts (searchKeyword) { + return new Promise((resolve, reject) => { + const params = { listAll: true, isrecursive: false, showicon: true, keyword: searchKeyword } + if (this.form.domainid) { + params.domainid = this.form.domainid + } + getAPI('listAccounts', params).then(json => { + let account = json?.listaccountsresponse?.account || [] + if (this.form.domainid) { + account = account.filter(a => a.domainid === this.form.domainid) + } + resolve({ + type: 'account', + data: account + }) + }).catch(error => { + reject(error.response.headers['x-description']) + }) + }) + }, + fetchHypervisors () { + return new Promise((resolve, reject) => { + getAPI('listHypervisors').then(json => { + const hypervisor = json.listhypervisorsresponse.hypervisor.map(a => { return { id: a.name, name: a.name } }) + resolve({ + type: 'hypervisor', + data: hypervisor + }) + }).catch(error => { + reject(error.response.headers['x-description']) + }) + }) + }, fetchImageStores (searchKeyword) { return new Promise((resolve, reject) => { - api('listImageStores', { listAll: true, showicon: true, keyword: searchKeyword }).then(json => { + getAPI('listImageStores', { listAll: true, showicon: true, keyword: searchKeyword }).then(json => { const imageStore = json.listimagestoresresponse.imagestore resolve({ type: 'imagestoreid', @@ -534,7 +996,7 @@ export default { }, fetchStoragePools (searchKeyword) { return new Promise((resolve, reject) => { - api('listStoragePools', { listAll: true, showicon: true, keyword: searchKeyword }).then(json => { + getAPI('listStoragePools', { listAll: true, showicon: true, keyword: searchKeyword }).then(json => { const storagePool = json.liststoragepoolsresponse.storagepool resolve({ type: 'storageid', @@ -547,7 +1009,7 @@ export default { }, fetchPods (searchKeyword) { return new Promise((resolve, reject) => { - api('listPods', { keyword: searchKeyword }).then(json => { + getAPI('listPods', { keyword: searchKeyword }).then(json => { const pods = json.listpodsresponse.pod resolve({ type: 'podid', @@ -560,7 +1022,7 @@ export default { }, fetchClusters (searchKeyword) { return new Promise((resolve, reject) => { - api('listClusters', { keyword: searchKeyword }).then(json => { + getAPI('listClusters', { keyword: searchKeyword }).then(json => { const clusters = json.listclustersresponse.cluster resolve({ type: 'clusterid', @@ -573,7 +1035,7 @@ export default { }, fetchInstanceGroups (searchKeyword) { return new Promise((resolve, reject) => { - api('listInstanceGroups', { listAll: true, keyword: searchKeyword }).then(json => { + getAPI('listInstanceGroups', { listAll: true, keyword: searchKeyword }).then(json => { const instancegroups = json.listinstancegroupsresponse.instancegroup resolve({ type: 'groupid', @@ -584,6 +1046,155 @@ export default { }) }) }, + fetchServiceOfferings (searchKeyword) { + return new Promise((resolve, reject) => { + getAPI('listServiceOfferings', { listAll: true, keyword: searchKeyword }).then(json => { + const serviceOfferings = json.listserviceofferingsresponse.serviceoffering + resolve({ + type: 'serviceofferingid', + data: serviceOfferings + }) + }).catch(error => { + reject(error.response.headers['x-description']) + }) + }) + }, + fetchDiskOfferings (searchKeyword) { + return new Promise((resolve, reject) => { + getAPI('listDiskOfferings', { listAll: true, keyword: searchKeyword }).then(json => { + const diskOfferings = json.listdiskofferingsresponse.diskoffering + resolve({ + type: 'diskofferingid', + data: diskOfferings + }) + }).catch(error => { + reject(error.response.headers['x-description']) + }) + }) + }, + fetchNetworks (searchKeyword) { + return new Promise((resolve, reject) => { + getAPI('listNetworks', { listAll: true, keyword: searchKeyword }).then(json => { + const networks = json.listnetworksresponse.network + resolve({ + type: 'networkid', + data: networks + }) + }).catch(error => { + reject(error.response.headers['x-description']) + }) + }) + }, + fetchAlertTypes () { + if (this.alertTypes.length > 0) { + return new Promise((resolve, reject) => { + resolve({ + type: 'type', + data: this.alertTypes + }) + }) + } else { + return new Promise((resolve, reject) => { + getAPI('listAlertTypes').then(json => { + const alerttypes = json.listalerttypesresponse.alerttype.map(a => { return { id: a.alerttypeid, name: a.name } }) + this.alertTypes = alerttypes + resolve({ + type: 'type', + data: alerttypes + }) + }).catch(error => { + reject(error.response.headers['x-description']) + }) + }) + } + }, + fetchAffinityGroupTypes () { + if (this.alertTypes.length > 0) { + return new Promise((resolve, reject) => { + resolve({ + type: 'type', + data: this.alertTypes + }) + }) + } else { + return new Promise((resolve, reject) => { + getAPI('listAffinityGroupTypes').then(json => { + const alerttypes = json.listaffinitygrouptypesresponse.affinityGroupType.map(a => { + let name = a.type + if (a.type === 'host anti-affinity') { + name = 'host anti-affinity (Strict)' + } else if (a.type === 'host affinity') { + name = 'host affinity (Strict)' + } else if (a.type === 'non-strict host anti-affinity') { + name = 'host anti-affinity (Non-Strict)' + } else if (a.type === 'non-strict host affinity') { + name = 'host affinity (Non-Strict)' + } + return { id: a.type, name: name } + }) + this.alertTypes = alerttypes + resolve({ + type: 'type', + data: alerttypes + }) + }).catch(error => { + reject(error.response.headers['x-description']) + }) + }) + } + }, + fetchVolumes (searchKeyword) { + return new Promise((resolve, reject) => { + getAPI('listVolumes', { listAll: true, isencrypted: searchKeyword }).then(json => { + const volumes = json.listvolumesresponse.volume + resolve({ + type: 'isencrypted', + data: volumes + }) + }).catch(error => { + reject(error.response.headers['x-description']) + }) + }) + }, + fetchVirtualMachines (searchKeyword) { + return new Promise((resolve, reject) => { + getAPI('listVirtualMachines', { listAll: true, keyword: searchKeyword }).then(json => { + const virtualMachines = json.listvirtualmachinesresponse.virtualmachine + resolve({ + type: 'virtualmachineid', + data: virtualMachines + }) + }).catch(error => { + reject(error.response.headers['x-description']) + }) + }) + }, + fetchManagementServers (searchKeyword) { + return new Promise((resolve, reject) => { + getAPI('listManagementServers', { listAll: true, keyword: searchKeyword }).then(json => { + const managementservers = json.listmanagementserversresponse.managementserver + resolve({ + type: 'managementserverid', + data: managementservers + }) + }).catch(error => { + reject(error.response.headers['x-description']) + }) + }) + }, + fetchOsCategories (searchKeyword) { + return new Promise((resolve, reject) => { + getAPI('listOsCategories', { showicon: true, keyword: searchKeyword }).then(json => { + const osCategories = json.listoscategoriesresponse.oscategory + resolve({ + type: 'oscategoryid', + data: osCategories + }) + }).catch(error => { + reject(error.response.headers['x-description']) + }) + }) + }, fetchGuestNetworkTypes () { const types = [] if (this.apiName.indexOf('listNetworks') > -1) { @@ -602,9 +1213,132 @@ export default { } return types }, + fetchAccountTypes () { + const types = [] + if (this.apiName.indexOf('listAccounts') > -1) { + types.push({ + id: '1', + name: 'Admin' + }) + types.push({ + id: '2', + name: 'DomainAdmin' + }) + types.push({ + id: '3', + name: 'User' + }) + } + return types + }, + fetchSystemVmTypes () { + const types = [] + if (this.apiName.indexOf('listSystemVms') > -1) { + types.push({ + id: 'consoleproxy', + name: 'label.console.proxy.vm' + }) + types.push({ + id: 'secondarystoragevm', + name: 'label.secondary.storage.vm' + }) + } + return types + }, + fetchStoragePoolScope () { + const types = [] + if (this.apiName.indexOf('listStoragePools') > -1) { + types.push({ + id: 'HOST', + name: 'label.hostname' + }) + types.push({ + id: 'CLUSTER', + name: 'label.cluster' + }) + types.push({ + id: 'ZONE', + name: 'label.zone' + }) + types.push({ + id: 'REGION', + name: 'label.region' + }) + types.push({ + id: 'GLOBAL', + name: 'label.global' + }) + } + return types + }, + fetchImageStoreProviders () { + const types = [] + if (this.apiName.indexOf('listImageStores') > -1) { + types.push({ + id: 'NFS', + name: 'NFS' + }) + types.push({ + id: 'SMB/CIFS', + name: 'SMB/CIFS' + }) + types.push({ + id: 'S3', + name: 'S3' + }) + types.push({ + id: 'Swift', + name: 'Swift' + }) + } + return types + }, + fetchRoleTypes () { + const types = [] + if (this.apiName.indexOf('listRoles') > -1) { + types.push({ + id: 'Admin', + name: 'Admin' + }) + types.push({ + id: 'ResourceAdmin', + name: 'ResourceAdmin' + }) + types.push({ + id: 'DomainAdmin', + name: 'DomainAdmin' + }) + types.push({ + id: 'User', + name: 'User' + }) + } + return types + }, + fetchScope () { + const scope = [] + if (this.apiName.indexOf('listWebhooks') > -1) { + scope.push({ + id: 'Local', + name: 'label.local' + }) + scope.push({ + id: 'Domain', + name: 'label.domain' + }) + if (isAdmin()) { + scope.push({ + id: 'Global', + name: 'label.global' + }) + } + } + return scope + }, fetchState () { + var state = [] if (this.apiName.includes('listVolumes')) { - return [ + state = [ { id: 'Allocated', name: 'label.allocated' @@ -630,8 +1364,89 @@ export default { name: 'label.migrating' } ] + } else if (this.apiName.includes('listKubernetesClusters')) { + state = [ + { + id: 'Created', + name: 'label.created' + }, + { + id: 'Starting', + name: 'label.starting' + }, + { + id: 'Running', + name: 'label.running' + }, + { + id: 'Stopping', + name: 'label.stopping' + }, + { + id: 'Stopped', + name: 'label.stopped' + }, + { + id: 'Scaling', + name: 'label.scaling' + }, + { + id: 'Upgrading', + name: 'label.upgrading' + }, + { + id: 'Alert', + name: 'label.alert' + }, + { + id: 'Recovering', + name: 'label.recovering' + }, + { + id: 'Destroyed', + name: 'label.destroyed' + }, + { + id: 'Destroying', + name: 'label.destroying' + }, + { + id: 'Error', + name: 'label.error' + } + ] + } else if (this.apiName.indexOf('listWebhooks') > -1) { + state = [ + { + id: 'Enabled', + name: 'label.enabled' + }, + { + id: 'Disabled', + name: 'label.disabled' + } + ] + } else if (this.apiName.indexOf('listEvents') > -1) { + state = [ + { + id: 'Created', + name: 'label.created' + }, + { + id: 'Scheduled', + name: 'label.scheduled' + }, + { + id: 'Started', + name: 'label.started' + }, + { + id: 'Completed', + name: 'label.completed' + } + ] } - return [] + return state }, fetchEntityType () { const entityType = [] @@ -688,12 +1503,99 @@ export default { }) return levels }, + fetchUsageTypes () { + return new Promise((resolve, reject) => { + getAPI('listUsageTypes') + .then(json => { + const usageTypes = json.listusagetypesresponse.usagetype.map(entry => { + return { + id: entry.id, + name: i18n.global.t(entry.name) + } + }) + + resolve({ + type: 'usagetype', + data: usageTypes + }) + }) + .catch(error => { + reject(error.response.headers['x-description']) + }) + }) + }, + fetchBackupOfferings (searchKeyword) { + return new Promise((resolve, reject) => { + getAPI('listBackupOfferings').then(json => { + const backupOfferings = json.listbackupofferingsresponse.backupoffering + resolve({ + type: 'backupofferingid', + data: backupOfferings + }) + }).catch(error => { + reject(error.response.headers['x-description']) + }) + }) + }, + fetchAvailableUserSourceTypes () { + return [ + { + id: 'native', + name: 'label.native' + }, + { + id: 'saml2', + name: 'label.saml' + }, + { + id: 'saml2disabled', + name: 'label.saml.disabled' + }, + { + id: 'ldap', + name: 'label.ldap' + } + ] + }, + fetchGpuCards (searchKeyword) { + return new Promise((resolve, reject) => { + getAPI('listGpuCards', { keyword: searchKeyword }).then(json => { + const gpuCards = json.listgpucardsresponse.gpucard + resolve({ + type: 'gpucardid', + data: gpuCards + }) + }).catch(error => { + reject(error.response.headers['x-description']) + }) + }) + }, + fetchVgpuProfiles (searchKeyword) { + return new Promise((resolve, reject) => { + const params = { keyword: searchKeyword } + + // If a GPU card is selected, filter vGPU profiles by that GPU card + if (this.form.gpucardid) { + params.gpucardid = this.form.gpucardid + } + + getAPI('listVgpuProfiles', params).then(json => { + const vgpuProfiles = json.listvgpuprofilesresponse.vgpuprofile + resolve({ + type: 'vgpuprofileid', + data: vgpuProfiles + }) + }).catch(error => { + reject(error.response.headers['x-description']) + }) + }) + }, onSearch (value) { this.paramsFilter = {} this.searchQuery = value this.$emit('search', { searchQuery: this.searchQuery }) }, - onClear () { + async onClear () { this.formRef.value.resetFields() this.form = reactive({}) this.isFiltered = false @@ -701,6 +1603,21 @@ export default { this.inputValue = null this.searchQuery = null this.paramsFilter = {} + + const refreshAccountOptions = ['account', 'domainid'].every((field) => { + return this.fields.some((searchViewField) => searchViewField.name === field) + }) + if (refreshAccountOptions) { + await this.fetchDynamicFieldData('account') + } + + const refreshVgpuProfileOptions = ['gpucardid', 'vgpuprofileid'].every((field) => { + return this.fields.some((searchViewField) => searchViewField.name === field) + }) + if (refreshVgpuProfileOptions) { + await this.fetchDynamicFieldData('vgpuprofileid') + } + this.$emit('search', this.paramsFilter) }, handleSubmit () { @@ -726,6 +1643,13 @@ export default { }, changeFilter (filter) { this.$emit('change-filter', filter) + }, + updateIsFiltered () { + this.isFiltered = this.searchFilters.some(item => { + if (this.searchParams[item]) { + return true + } + }) } } } diff --git a/ui/src/components/view/SettingsTab.vue b/ui/src/components/view/SettingsTab.vue index cddbe56b83a9..0e0ee33f2800 100644 --- a/ui/src/components/view/SettingsTab.vue +++ b/ui/src/components/view/SettingsTab.vue @@ -17,71 +17,29 @@ + + diff --git a/ui/src/components/view/TreeView.vue b/ui/src/components/view/TreeView.vue index 40cd6f0f9672..f767b7adef2a 100644 --- a/ui/src/components/view/TreeView.vue +++ b/ui/src/components/view/TreeView.vue @@ -92,7 +92,7 @@ diff --git a/ui/src/components/view/VmwareData.vue b/ui/src/components/view/VmwareData.vue index efb921df5f30..e978fbb29da7 100644 --- a/ui/src/components/view/VmwareData.vue +++ b/ui/src/components/view/VmwareData.vue @@ -35,7 +35,7 @@ + + diff --git a/ui/src/components/view/WebhookFiltersTab.vue b/ui/src/components/view/WebhookFiltersTab.vue new file mode 100644 index 000000000000..2d276e2a3cf0 --- /dev/null +++ b/ui/src/components/view/WebhookFiltersTab.vue @@ -0,0 +1,416 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT 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/FilterStats.vue b/ui/src/components/view/stats/FilterStats.vue deleted file mode 100644 index 20cd63af873d..000000000000 --- a/ui/src/components/view/stats/FilterStats.vue +++ /dev/null @@ -1,165 +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/components/view/stats/ResourceStatsInfo.vue b/ui/src/components/view/stats/ResourceStatsInfo.vue index 9db3384bc5f8..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. @@ -45,7 +45,8 @@ export default { { resourceType: 'CHART', messageList: [ - this.$t('message.chart.statistic.info') + this.$t('message.chart.statistic.info'), + this.$t('message.chart.statistic.info.hypervisor.additionals') ] }, { diff --git a/ui/src/components/view/stats/ResourceStatsLineChart.vue b/ui/src/components/view/stats/ResourceStatsLineChart.vue index fa15ea398a5e..399e77bebd46 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. diff --git a/ui/src/components/widgets/BlockRadioGroupSelect.vue b/ui/src/components/widgets/BlockRadioGroupSelect.vue new file mode 100644 index 000000000000..a2364597a63d --- /dev/null +++ b/ui/src/components/widgets/BlockRadioGroupSelect.vue @@ -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. + + + + + + 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 ae0a034de02d..edee7498991a 100644 --- a/ui/src/components/widgets/Console.vue +++ b/ui/src/components/widgets/Console.vue @@ -17,9 +17,17 @@ @@ -29,7 +29,10 @@ - - diff --git a/ui/src/components/widgets/ResourceLabel.vue b/ui/src/components/widgets/ResourceLabel.vue index c200c6a66550..0a10843ff86d 100644 --- a/ui/src/components/widgets/ResourceLabel.vue +++ b/ui/src/components/widgets/ResourceLabel.vue @@ -18,7 +18,13 @@ diff --git a/ui/src/locales/index.js b/ui/src/locales/index.js index 29c828b64472..6933e05206e1 100644 --- a/ui/src/locales/index.js +++ b/ui/src/locales/index.js @@ -39,7 +39,7 @@ export function loadLanguageAsync (lang) { return Promise.resolve(setLanguage(lang)) } - return fetch(`locales/${lang}.json`) + return fetch(`locales/${lang}.json?ts=${Date.now()}`) .then(response => response.json()) .then(json => Promise.resolve(setLanguage(lang, json))) } diff --git a/ui/src/main.js b/ui/src/main.js index 2f1d892fbd86..7441f8010865 100644 --- a/ui/src/main.js +++ b/ui/src/main.js @@ -15,6 +15,7 @@ // specific language governing permissions and limitations // under the License. +import { createApp, h } from 'vue' import { vueApp, vueProps } from './vue-app' import router from './router' import store from './store' @@ -35,10 +36,18 @@ import { resourceTypePlugin, fileSizeUtilPlugin, genericUtilPlugin, - localesPlugin + localesPlugin, + dialogUtilPlugin, + cpuArchitectureUtilPlugin, + imagesUtilPlugin, + extensionsUtilPlugin, + backupUtilPlugin } from './utils/plugins' import { VueAxios } from './utils/request' import directives from './utils/directives' +import Cookies from 'js-cookie' +import { getAPI } from '@/api' +import { applyCustomGuiTheme } from './utils/guiTheme' vueApp.use(VueAxios, router) vueApp.use(pollJobPlugin) @@ -51,23 +60,71 @@ vueApp.use(resourceTypePlugin) vueApp.use(fileSizeUtilPlugin) vueApp.use(localesPlugin) vueApp.use(genericUtilPlugin) +vueApp.use(dialogUtilPlugin) +vueApp.use(cpuArchitectureUtilPlugin) +vueApp.use(imagesUtilPlugin) +vueApp.use(extensionsUtilPlugin) +vueApp.use(backupUtilPlugin) vueApp.use(extensions) vueApp.use(directives) -fetch('config.json').then(response => response.json()).then(config => { - vueProps.$config = config - let basUrl = config.apiBase - if (config.multipleServer) { - basUrl = (config.servers[0].apiHost || '') + config.servers[0].apiBase +const renderError = (err) => { + console.error('Fatal error during app initialization: ', err) + const ErrorComponent = { + render: () => h( + 'div', + { style: 'font-family: sans-serif; text-align: center; padding: 2rem;' }, + [ + h('h2', { style: 'color: #ff4d4f;' }, 'We\'re experiencing a problem'), + h('p', 'The application could not be loaded due to a configuration issue. Please try again later.'), + h('details', { style: 'margin-top: 20px;' }, [ + h('summary', { style: 'cursor: pointer;' }, 'Technical details'), + h('pre', { + style: 'text-align: left; display: inline-block; margin-top: 10px;' + }, 'Missing or malformed config.json. Please ensure the file is present, accessible, and contains valid JSON. Check the browser console for more information.') + ]) + ] + ) } + createApp(ErrorComponent).mount('#app') +} - vueProps.axios.defaults.baseURL = basUrl +fetch('config.json?ts=' + Date.now()) + .then(response => { + if (!response.ok) { + throw new Error(`Failed to fetch config.json: ${response.status} ${response.statusText}`) + } + return response.json() + }) + .then(async config => { + vueProps.$config = config + let baseUrl = config.apiBase + if (config.multipleServer) { + baseUrl = (config.servers[0].apiHost || '') + config.servers[0].apiBase + } + + vueProps.axios.defaults.baseURL = baseUrl + + const userid = Cookies.get('userid') + let accountid = null + let domainid = null + + if (userid !== undefined && Cookies.get('sessionkey')) { + await getAPI('listUsers', { userid: userid }).then(response => { + accountid = response.listusersresponse.user[0].accountid + domainid = response.listusersresponse.user[0].domainid + }) + } + + await applyCustomGuiTheme(accountid, domainid) - loadLanguageAsync().then(() => { - vueApp.use(store) - .use(router) - .use(i18n) - .use(bootstrap) - .mount('#app') + loadLanguageAsync().then(() => { + vueApp.use(store) + .use(router) + .use(i18n) + .use(bootstrap) + .mount('#app') + }) + }).catch(error => { + renderError(error) }) -}) diff --git a/ui/src/permission.js b/ui/src/permission.js index 6db690f6e6ad..671d6626b931 100644 --- a/ui/src/permission.js +++ b/ui/src/permission.js @@ -30,7 +30,7 @@ import { ACCESS_TOKEN, APIS, SERVER_MANAGER, CURRENT_PROJECT } from '@/store/mut NProgress.configure({ showSpinner: false }) // NProgress Configuration -const allowList = ['login', 'VerifyOauth'] // no redirect allowlist +const allowList = ['login', 'VerifyOauth', 'forgotPassword', 'resetPassword'] // no redirect allowlist router.beforeEach((to, from, next) => { // start progress bar @@ -94,6 +94,16 @@ router.beforeEach((to, from, next) => { } store.commit('SET_LOGIN_FLAG', true) } + // 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, {}) if (Object.keys(cachedApis).length > 0) { @@ -102,6 +112,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) @@ -112,7 +135,11 @@ router.beforeEach((to, from, next) => { } else { next({ path: redirect }) } - const project = vueProps.$localStorage.get(CURRENT_PROJECT) + var project = vueProps.$localStorage.get(CURRENT_PROJECT) + if (project == null) { + project = {} + store.commit('SET_PROJECT', project) + } store.dispatch('ToggleTheme', project.id === undefined ? 'light' : 'dark') }) }) diff --git a/ui/src/store/getters.js b/ui/src/store/getters.js index 67b168be8c28..c7ab2f0c536b 100644 --- a/ui/src/store/getters.js +++ b/ui/src/store/getters.js @@ -28,6 +28,7 @@ const getters = { apis: state => state.user.apis, features: state => state.user.features, userInfo: state => state.user.info, + latestVersion: state => state.user.latestVersion, addRouters: state => state.permission.addRouters, multiTab: state => state.app.multiTab, listAllProjects: state => state.app.listAllProjects, @@ -35,6 +36,7 @@ const getters = { isLdapEnabled: state => state.user.isLdapEnabled, cloudian: state => state.user.cloudian, zones: state => state.user.zones, + showSecurityGroups: state => state.user.showSecurityGroups, timezoneoffset: state => state.user.timezoneoffset, usebrowsertimezone: state => state.user.usebrowsertimezone, server: state => state.app.server, @@ -44,6 +46,8 @@ const getters = { countNotify: state => state.user.countNotify, customColumns: state => state.user.customColumns, logoutFlag: state => state.user.logoutFlag, + msId: state => state.user.msId, + maintenanceInitiated: state => state.user.maintenanceInitiated, shutdownTriggered: state => state.user.shutdownTriggered, twoFaEnabled: state => state.user.twoFaEnabled, twoFaProvider: state => state.user.twoFaProvider, @@ -51,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/app.js b/ui/src/store/modules/app.js index cf2b34e4b8e7..f6d66870d968 100644 --- a/ui/src/store/modules/app.js +++ b/ui/src/store/modules/app.js @@ -128,6 +128,9 @@ const app = { vueProps.$localStorage.set(RELOAD_ALL_PROJECTS, allProjects) state.allProjects = allProjects }, + SET_MAINTENANCE_INITIATED: (state, maintenanceInitiated) => { + state.maintenanceInitiated = maintenanceInitiated + }, SET_SHUTDOWN_TRIGGERED: (state, shutdownTriggered) => { state.shutdownTriggered = shutdownTriggered }, @@ -193,6 +196,9 @@ const app = { ReloadAllProjects ({ commit, allProjects }) { commit('RELOAD_ALL_PROJECTS', allProjects) }, + SetMaintenanceInitiated ({ commit }, bool) { + commit('SET_MAINTENANCE_INITIATED', bool) + }, SetShutdownTriggered ({ commit }, bool) { commit('SET_SHUTDOWN_TRIGGERED', bool) }, diff --git a/ui/src/store/modules/permission.js b/ui/src/store/modules/permission.js index 3b080be5f636..ad11fe5a8d0f 100644 --- a/ui/src/store/modules/permission.js +++ b/ui/src/store/modules/permission.js @@ -17,24 +17,39 @@ import { asyncRouterMap, constantRouterMap } from '@/config/router' -function hasApi (apis, route) { - if (route.meta && route.meta.permission) { - for (const permission of route.meta.permission) { - if (!apis.includes(permission)) { - return false - } - } +function hasAccessToRoute (apis, route) { + if (!route.meta || !route.meta.permission) { return true } + for (const permission of route.meta.permission) { + if (!apis.includes(permission)) { + return false + } + } + return true +} + +function hasAccessToSection (route) { + const visibleChildren = route.children.filter(child => !child.hidden) + if (visibleChildren.length === 0) { + return false + } + const redirect = '/' + visibleChildren[0].meta.name + if (redirect !== route.path) { + route.redirect = redirect + } return true } function filterAsyncRouter (routerMap, apis) { const accessedRouters = routerMap.filter(route => { - if (hasApi(apis, route)) { + if (hasAccessToRoute(apis, route)) { if (route.children && route.children.length > 0) { route.children = filterAsyncRouter(route.children, apis) } + if (route.meta && route.meta.section) { + return hasAccessToSection(route) + } return true } return false diff --git a/ui/src/store/modules/user.js b/ui/src/store/modules/user.js index fb5b6ff5e0bb..6a818d587233 100644 --- a/ui/src/store/modules/user.js +++ b/ui/src/store/modules/user.js @@ -18,12 +18,15 @@ import Cookies from 'js-cookie' import message from 'ant-design-vue/es/message' import notification from 'ant-design-vue/es/notification' +import semver from 'semver' import { vueProps } from '@/vue-app' import router from '@/router' import store from '@/store' -import { oauthlogin, login, logout, api } from '@/api' +import { oauthlogin, login, logout, getAPI } from '@/api' import { i18n } from '@/locales' +import { axios } from '../../utils/request' +import { getParsedVersion } from '@/utils/util' import { ACCESS_TOKEN, @@ -31,16 +34,24 @@ import { DEFAULT_THEME, APIS, ZONES, + SHOW_SECURTIY_GROUPS, TIMEZONE_OFFSET, USE_BROWSER_TIMEZONE, HEADER_NOTICES, DOMAIN_STORE, DARK_MODE, CUSTOM_COLUMNS, + MS_ID, OAUTH_DOMAIN, - OAUTH_PROVIDER + OAUTH_PROVIDER, + LATEST_CS_VERSION, + PASSWORD_CHANGE_REQUIRED } from '@/store/mutation-types' +import { + applyCustomGuiTheme +} from '@/utils/guiTheme' + const user = { state: { token: '', @@ -63,12 +74,15 @@ const user = { loginFlag: false, logoutFlag: false, customColumns: {}, + msId: '', + maintenanceInitiated: false, shutdownTriggered: false, twoFaEnabled: false, twoFaProvider: '', twoFaIssuer: '', customHypervisorName: 'Custom', - readyForShutdownPollingJob: '' + readyForShutdownPollingJob: '', + passwordChangeRequired: false }, mutations: { @@ -120,6 +134,10 @@ const user = { state.zones = zones vueProps.$localStorage.set(ZONES, zones) }, + SET_SHOW_SECURITY_GROUPS: (state, show) => { + state.showSecurityGroups = show + vueProps.$localStorage.set(SHOW_SECURTIY_GROUPS, show) + }, SET_DOMAIN_STORE (state, domainStore) { state.domainStore = domainStore vueProps.$localStorage.set(DOMAIN_STORE, domainStore) @@ -138,6 +156,13 @@ const user = { vueProps.$localStorage.set(CUSTOM_COLUMNS, customColumns) state.customColumns = customColumns }, + SET_MS_ID: (state, msId) => { + state.msId = msId + vueProps.$localStorage.set(MS_ID, msId) + }, + SET_MAINTENANCE_INITIATED: (state, maintenanceInitiated) => { + state.maintenanceInitiated = maintenanceInitiated + }, SET_SHUTDOWN_TRIGGERED: (state, shutdownTriggered) => { state.shutdownTriggered = shutdownTriggered }, @@ -167,6 +192,20 @@ const user = { }, SET_OAUTH_PROVIDER_USED_TO_LOGIN: (state, provider) => { vueProps.$localStorage.set(OAUTH_PROVIDER, provider) + }, + SET_LATEST_VERSION: (state, version) => { + if (version?.fetchedTs > 0) { + 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) + } } }, @@ -212,8 +251,19 @@ const user = { commit('SET_2FA_PROVIDER', result.providerfor2fa) commit('SET_2FA_ISSUER', result.issuerfor2fa) commit('SET_LOGIN_FLAG', false) + 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() - resolve() }).catch(error => { reject(error) @@ -259,6 +309,11 @@ const user = { commit('SET_2FA_PROVIDER', result.providerfor2fa) commit('SET_2FA_ISSUER', result.issuerfor2fa) commit('SET_LOGIN_FLAG', false) + if (result && result.managementserverid) { + commit('SET_MS_ID', result.managementserverid) + } + const latestVersion = vueProps.$localStorage.get(LATEST_CS_VERSION, { version: '', fetchedTs: 0 }) + commit('SET_LATEST_VERSION', latestVersion) notification.destroy() resolve() @@ -276,21 +331,36 @@ const user = { const cachedUseBrowserTimezone = vueProps.$localStorage.get(USE_BROWSER_TIMEZONE, false) const cachedCustomColumns = vueProps.$localStorage.get(CUSTOM_COLUMNS, {}) const domainStore = vueProps.$localStorage.get(DOMAIN_STORE, {}) + const cachedShowSecurityGroups = vueProps.$localStorage.get(SHOW_SECURTIY_GROUPS, false) const darkMode = vueProps.$localStorage.get(DARK_MODE, false) + const msId = vueProps.$localStorage.get(MS_ID, false) + const latestVersion = vueProps.$localStorage.get(LATEST_CS_VERSION, { version: '', fetchedTs: 0 }) const hasAuth = Object.keys(cachedApis).length > 0 commit('SET_DOMAIN_STORE', domainStore) commit('SET_DARK_MODE', darkMode) + commit('SET_LATEST_VERSION', latestVersion) + + // 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) + commit('SET_SHOW_SECURITY_GROUPS', cachedShowSecurityGroups) commit('SET_APIS', cachedApis) commit('SET_TIMEZONE_OFFSET', cachedTimezoneOffset) commit('SET_USE_BROWSER_TIMEZONE', cachedUseBrowserTimezone) commit('SET_CUSTOM_COLUMNS', cachedCustomColumns) + commit('SET_MS_ID', msId) // Ensuring we get the user info so that store.getters.user is never empty when the page is freshly loaded - api('listUsers', { username: Cookies.get('username'), listall: true }).then(response => { + getAPI('listUsers', { id: Cookies.get('userid'), listall: true }).then(response => { const result = response.listusersresponse.user[0] commit('SET_INFO', result) commit('SET_NAME', result.firstname + ' ' + result.lastname) @@ -300,13 +370,13 @@ const user = { }) } else if (store.getters.loginFlag) { const hide = message.loading(i18n.global.t('message.discovering.feature'), 0) - api('listZones').then(json => { + getAPI('listZones').then(json => { const zones = json.listzonesresponse.zone || [] commit('SET_ZONES', zones) }).catch(error => { reject(error) }) - api('listApis').then(response => { + getAPI('listApis').then(response => { const apis = {} const apiList = response.listapisresponse.api for (var idx = 0; idx < apiList.length; idx++) { @@ -314,7 +384,10 @@ const user = { const apiName = api.name apis[apiName] = { params: api.params, - response: api.response + response: api.response, + isasync: api.isasync, + since: api.since, + description: api.description } } commit('SET_APIS', apis) @@ -329,17 +402,57 @@ const user = { }).catch(error => { reject(error) }) + + getAPI('listNetworks', { restartrequired: true, forvpc: false }).then(response => { + if (response.listnetworksresponse.count > 0) { + store.dispatch('AddHeaderNotice', { + key: 'NETWORK_RESTART_REQUIRED', + title: i18n.global.t('label.network.restart.required'), + description: i18n.global.t('message.network.restart.required'), + path: '/guestnetwork/', + query: { restartrequired: true, forvpc: false }, + status: 'done', + timestamp: new Date() + }) + } + }).catch(ignored => {}) + + getAPI('listVPCs', { restartrequired: true }).then(response => { + if (response.listvpcsresponse.count > 0) { + store.dispatch('AddHeaderNotice', { + key: 'VPC_RESTART_REQUIRED', + title: i18n.global.t('label.vpc.restart.required'), + description: i18n.global.t('message.vpc.restart.required'), + path: '/vpc/', + query: { restartrequired: true }, + status: 'done', + timestamp: new Date() + }) + } + }).catch(ignored => {}) } - api('listUsers', { username: Cookies.get('username') }).then(response => { + getAPI('listUsers', { id: Cookies.get('userid'), showicon: true }).then(response => { const result = response.listusersresponse.user[0] + applyCustomGuiTheme(result.accountid, result.domainid) commit('SET_INFO', result) commit('SET_NAME', result.firstname + ' ' + result.lastname) + commit('SET_AVATAR', result.icon?.base64image || '') + store.dispatch('SetCsLatestVersion', result.rolename) }).catch(error => { reject(error) }) - api('listCapabilities').then(response => { + getAPI( + 'listNetworkServiceProviders', + { name: 'SecurityGroupProvider', state: 'Enabled' } + ).then(response => { + const showSecurityGroups = response.listnetworkserviceprovidersresponse.count > 0 + commit('SET_SHOW_SECURITY_GROUPS', showSecurityGroups) + }).catch(ignored => { + }) + + getAPI('listCapabilities').then(response => { const result = response.listcapabilitiesresponse.capability commit('SET_FEATURES', result) if (result && result.defaultuipagesize) { @@ -348,22 +461,27 @@ const user = { 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) }) - api('listLdapConfigurations').then(response => { + getAPI('listLdapConfigurations').then(response => { const ldapEnable = (response.ldapconfigurationresponse.count > 0) commit('SET_LDAP', ldapEnable) }).catch(error => { reject(error) }) - api('cloudianIsEnabled').then(response => { + getAPI('cloudianIsEnabled').then(response => { const cloudian = response.cloudianisenabledresponse.cloudianisenabled || {} commit('SET_CLOUDIAN', cloudian) }).catch(ignored => { }) + }).catch(error => { + console.error(error) }) }, @@ -374,11 +492,6 @@ const user = { cloudianUrl = state.cloudian.url + 'logout.htm?redirect=' + encodeURIComponent(window.location.href) } - Object.keys(Cookies.get()).forEach(cookieName => { - Cookies.remove(cookieName) - Cookies.remove(cookieName, { path: '/client' }) - }) - commit('SET_TOKEN', '') commit('SET_APIS', {}) commit('SET_PROJECT', {}) @@ -393,10 +506,13 @@ const user = { commit('SET_2FA_PROVIDER', '') commit('SET_2FA_ISSUER', '') commit('SET_LOGIN_FLAG', false) + commit('SET_MS_ID', '') vueProps.$localStorage.remove(CURRENT_PROJECT) vueProps.$localStorage.remove(ACCESS_TOKEN) vueProps.$localStorage.remove(HEADER_NOTICES) + commit('SET_PASSWORD_CHANGE_REQUIRED', false) + logout(state.token).then(() => { message.destroy() if (cloudianUrl) { @@ -406,6 +522,19 @@ const user = { } }).catch(() => { resolve() + }).finally(() => { + const paths = ['/', '/client'] + const hostname = window.location.hostname + const domains = [undefined, hostname, `.${hostname}`] + Object.keys(Cookies.get()).forEach(cookieName => { + paths.forEach(path => { + domains.forEach(domain => { + const options = { path } + if (domain) options.domain = domain + Cookies.remove(cookieName, options) + }) + }) + }) }) }) }, @@ -429,7 +558,7 @@ const user = { }, ProjectView ({ commit }, projectid) { return new Promise((resolve, reject) => { - api('listApis', { projectid: projectid }).then(response => { + getAPI('listApis', { projectid: projectid }).then(response => { const apis = {} const apiList = response.listapisresponse.api for (var idx = 0; idx < apiList.length; idx++) { @@ -454,27 +583,28 @@ const user = { }, RefreshFeatures ({ commit }) { return new Promise((resolve, reject) => { - api('listCapabilities').then(response => { + getAPI('listCapabilities').then(response => { const result = response.listcapabilitiesresponse.capability resolve(result) commit('SET_FEATURES', result) }).catch(error => { reject(error) }) - - api('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) - }) + 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 }) { return new Promise((resolve, reject) => { - api('listLdapConfigurations').then(response => { + getAPI('listLdapConfigurations').then(response => { const ldapEnable = (response.ldapconfigurationresponse.count > 0) commit('SET_LDAP', ldapEnable) }).catch(error => { @@ -485,6 +615,32 @@ const user = { SetDomainStore ({ commit }, domainStore) { 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( + 'https://api.github.com/repos/apache/cloudstack/releases' + ).then(response => { + let latestReleaseVersion = getParsedVersion(response[0].tag_name) + let latestTag = response[0].tag_name + + for (const release of response) { + if (release.tag_name.toLowerCase().includes('rc')) { + continue + } + const parsedVersion = getParsedVersion(release.tag_name) + if (semver.gte(parsedVersion, latestReleaseVersion)) { + latestReleaseVersion = parsedVersion + latestTag = release.tag_name + commit('SET_LATEST_VERSION', { version: latestTag, fetchedTs: (+new Date()) }) + } + } + }).catch(ignored => {}) + } + }, SetDarkMode ({ commit }, darkMode) { commit('SET_DARK_MODE', darkMode) }, diff --git a/ui/src/store/mutation-types.js b/ui/src/store/mutation-types.js index 77aeb8fb7b6f..5fc2cd74d213 100644 --- a/ui/src/store/mutation-types.js +++ b/ui/src/store/mutation-types.js @@ -29,17 +29,21 @@ export const DEFAULT_CONTENT_WIDTH_TYPE = 'DEFAULT_CONTENT_WIDTH_TYPE' export const DEFAULT_MULTI_TAB = 'DEFAULT_MULTI_TAB' export const APIS = 'APIS' export const ZONES = 'ZONES' +export const SHOW_SECURTIY_GROUPS = 'SHOW_SECURITY_GROUPS' export const HEADER_NOTICES = 'HEADER_NOTICES' export const TIMEZONE_OFFSET = 'TIMEZONE_OFFSET' export const USE_BROWSER_TIMEZONE = 'USE_BROWSER_TIMEZONE' export const SERVER_MANAGER = 'SERVER_MANAGER' export const DOMAIN_STORE = 'DOMAIN_STORE' export const DARK_MODE = 'DARK_MODE' +export const LATEST_CS_VERSION = 'LATEST_CS_VERSION' export const VUE_VERSION = 'VUE_VERSION' export const CUSTOM_COLUMNS = 'CUSTOM_COLUMNS' 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 391c66565d4b..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 @@ -5,7 +24,7 @@ ## main .less entry points: 1. dist/antd.less - - imports everthing with index.less + components.less + - imports everything with index.less + components.less 2. lib/style/index.less - themes/default.less - color/colors' @@ -13,7 +32,7 @@ - core/index.less - includes base styles, motion rules and iconfont -# src/style/ explaination +# src/style/ explanation - index.less includes ant styles, as well as all custom variables and rules @@ -25,7 +44,7 @@ - include all rules that reset styles, define global stuffs without classes at all - e.g. body {} p, ul, li {} h1, h2, h3 {} 3. ant-overwrite - - any styles that overwrites the existin ant rules by any reason + - any styles that overwrites the existing ant rules by any reason - e.g. classes like .ant-layout-header .anticon {} 4. frame - everything that belongs to the frame @@ -34,11 +53,10 @@ - rules that modify the page at all if new layout class is set. - e.g. #html class="layout-ant-black"# 6. objects - - repeatly used elements like buttons, inputs + - repeatedly used elements like buttons, inputs 7. components - complex elements like dropdown, forms, table, search (usually include this to components/FooterToolbar/ folder) - # The "/deep/" combinator - use the /deep/ combinator (or in other versions ">>>") helps us to exclude "scoped" rules into global - e.g. will scope a generated data ID like .a .b .c[data-abcde] {} diff --git a/ui/src/style/objects/form.scss b/ui/src/style/objects/form.scss new file mode 100644 index 000000000000..290a3b6e78cc --- /dev/null +++ b/ui/src/style/objects/form.scss @@ -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. + +.form { + width: 80vw; + + @media (min-width: 500px) { + width: fit-content; + min-width: 20vw; + } +} + +.border-success { + border-color: #349469; +} + +.border-fail { + border-color: #dc3545; +} + +.form textarea { + resize: both; + min-width: 20vw; + max-width: 80vw; +} + +.ant-input-number, +.ant-calendar-picker { + width: 100%; +} diff --git a/ui/src/style/vars.less b/ui/src/style/vars.less index fc6fdf751702..de2d494c878f 100644 --- a/ui/src/style/vars.less +++ b/ui/src/style/vars.less @@ -471,6 +471,10 @@ a { width: auto; } +.ant-list-item.selected-item { + background-color: @primary-color-light; +} + .ant-select-arrow .anticon { vertical-align: top; } 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/date.js b/ui/src/utils/date.js new file mode 100644 index 000000000000..216dfde1303b --- /dev/null +++ b/ui/src/utils/date.js @@ -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. +import store from '@/store' + +import dayjs from 'dayjs' +import utc from 'dayjs/plugin/utc' + +dayjs.extend(utc) + +export function parseDayJsObject ({ value, format = true, keepMoment = true }) { + if (!value) { + return null + } + + if (typeof value === 'string') { + value = dayjs(value) + } + + if (!store.getters.usebrowsertimezone) { + value = value.utc(keepMoment) + } + + if (!format) { + return value + } + + return value.format() +} + +/** + * When passing a string/dayjs to the date picker component, it converts the value to the browser timezone; therefore, + * we need to normalize the value to UTC if user is not using browser's timezone. + * @param {*} value The datetime to normalize. + * @returns A dayjs object with the datetime normalized to UTC if user is not using browser's timezone; + * otherwise, a correspondent dayjs object based on the value passed. + */ +export function parseDateToDatePicker (value) { + if (!value) { + return null + } + + if (typeof value === 'string') { + value = dayjs(value) + } + + if (store.getters.usebrowsertimezone) { + return value + } + + return value.utc(false) +} + +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 + } + + 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 toLocaleDate ({ date, timezoneoffset = store.getters.timezoneoffset, usebrowsertimezone = store.getters.usebrowsertimezone, dateOnly = false, hourOnly = false }) { + if (!date) { + return null + } + + let dateWithOffset = toLocalDate({ date, timezoneoffset, usebrowsertimezone }).toUTCString() + + // e.g. "Mon, 03 Jun 2024 19:22:55 GMT" -> "03 Jun 2024 19:22:55 GMT" + dateWithOffset = dateWithOffset.substring(dateWithOffset.indexOf(', ') + 2) + + // e.g. "03 Jun 2024 19:22:55 GMT" -> "03 Jun 2024 19:22:55" + dateWithOffset = dateWithOffset.substring(0, dateWithOffset.length - 4) + + if (dateOnly) { + // e.g. "03 Jun 2024 19:22:55" -> "03 Jun 2024" + return dateWithOffset.substring(0, dateWithOffset.length - 9) + } + + if (hourOnly) { + // e.g. "03 Jun 2024 19:22:55" -> "19:22:55" + return dateWithOffset.substring(dateWithOffset.length - 8, dateWithOffset.length) + } + + return dateWithOffset +} + +export { dayjs } diff --git a/ui/src/utils/extension.js b/ui/src/utils/extension.js new file mode 100644 index 000000000000..cd8d0c4dacac --- /dev/null +++ b/ui/src/utils/extension.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. + +export function getFilteredExternalDetails (details) { + if (!details || typeof details !== 'object') { + return null + } + const prefix = 'External:' + const result = {} + for (const key in details) { + if (key.startsWith(prefix)) { + result[key.substring(prefix.length)] = details[key] + } + } + return Object.keys(result).length > 0 ? result : null +} diff --git a/ui/src/utils/guiTheme.js b/ui/src/utils/guiTheme.js new file mode 100644 index 000000000000..b1a7209fd270 --- /dev/null +++ b/ui/src/utils/guiTheme.js @@ -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 +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT 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 { vueProps } from '@/vue-app' +import { getAPI } from '@/api' + +export async function applyCustomGuiTheme (accountid, domainid) { + await fetch('config.json').then(response => response.json()).then(config => { + vueProps.$config = config + }) + + let guiTheme + + if (accountid != null) { + guiTheme = await fetchGuiTheme({ accountid: accountid }) + } + + if (guiTheme === undefined && domainid != null) { + guiTheme = await fetchGuiTheme({ domainid: domainid }) + } + + if (guiTheme === undefined) { + guiTheme = await fetchGuiTheme({ commonname: window.location.hostname }) + } + + if (guiTheme === undefined) { + guiTheme = await fetchGuiTheme({ listonlydefaulttheme: true }) + } + + await applyDynamicCustomization(guiTheme) +} + +async function fetchGuiTheme (params) { + return await getAPI('listGuiThemes', params).then(response => { + if (response.listguithemesresponse.guiThemes) { + return response.listguithemesresponse.guiThemes[0] + } + }).catch(error => { + console.error('Error fetching GUI theme:', error) + return null + }) +} + +async function applyDynamicCustomization (response) { + let jsonConfig + + if (response?.jsonconfiguration) { + jsonConfig = JSON.parse(response?.jsonconfiguration) + } + + // 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 + vueProps.$config.loginFooter = jsonConfig?.loginFooter ?? vueProps.$config.loginFooter + vueProps.$config.logo = jsonConfig?.logo ?? vueProps.$config.logo + vueProps.$config.minilogo = jsonConfig?.minilogo ?? vueProps.$config.minilogo + vueProps.$config.banner = jsonConfig?.banner ?? vueProps.$config.banner + + 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] + } + + if (jsonConfig?.plugins) { + jsonConfig.plugins.forEach(plugin => { + vueProps.$config.plugins.push(plugin) + }) + } + + vueProps.$config.favicon = jsonConfig?.favicon ?? vueProps.$config.favicon + vueProps.$config.css = response?.css ?? null + + await applyStaticCustomization(vueProps.$config.favicon, vueProps.$config.css) +} + +async function applyStaticCustomization (favicon, css) { + document.getElementById('favicon').href = favicon + + let style = document.getElementById('guiThemeCSS') + if (style != null) { + style.innerHTML = css + } else { + style = document.createElement('style') + style.setAttribute('id', 'guiThemeCSS') + style.innerHTML = css + document.body.appendChild(style) + } +} diff --git a/ui/src/utils/links.js b/ui/src/utils/links.js new file mode 100644 index 000000000000..fa650cdd7b8b --- /dev/null +++ b/ui/src/utils/links.js @@ -0,0 +1,36 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +export function validateLinks (router, isStatic, resource) { + const validLinks = { + volume: false + } + + 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 + } + } + + return validLinks +} diff --git a/ui/src/utils/network.js b/ui/src/utils/network.js new file mode 100644 index 000000000000..c3054f48ff77 --- /dev/null +++ b/ui/src/utils/network.js @@ -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. + +// Parsing CIDR into Gateway,Netmask Placeholders + +export function getNetmaskFromCidr (cidr) { + if (!cidr?.includes('/')) return undefined + const [, maskBits] = cidr.split('/') + const subnetMasks = { + 8: '255.0.0.0', + 9: '255.128.0.0', + 10: '255.192.0.0', + 11: '255.224.0.0', + 12: '255.240.0.0', + 13: '255.248.0.0', + 14: '255.252.0.0', + 15: '255.254.0.0', + 16: '255.255.0.0', + 17: '255.255.128.0', + 18: '255.255.192.0', + 19: '255.255.224.0', + 20: '255.255.240.0', + 21: '255.255.248.0', + 22: '255.255.252.0', + 23: '255.255.254.0', + 24: '255.255.255.0', + 25: '255.255.255.128', + 26: '255.255.255.192', + 27: '255.255.255.224', + 28: '255.255.255.240', + 29: '255.255.255.248', + 30: '255.255.255.252', + 31: '255.255.255.254', + 32: '255.255.255.255' + } + return subnetMasks[+maskBits] || '255.255.255.0' +} diff --git a/ui/src/utils/plugins.js b/ui/src/utils/plugins.js index 7223f0503684..b8856c7c9b00 100644 --- a/ui/src/utils/plugins.js +++ b/ui/src/utils/plugins.js @@ -17,11 +17,12 @@ import _ from 'lodash' import { i18n } from '@/locales' -import { api } from '@/api' -import { message, notification } from 'ant-design-vue' +import { getAPI } from '@/api' +import { message, notification, Modal } from 'ant-design-vue' import eventBus from '@/config/eventBus' import store from '@/store' import { sourceToken } from '@/utils/request' +import { toLocalDate, toLocaleDate } from '@/utils/date' export const pollJobPlugin = { install (app) { @@ -31,6 +32,7 @@ export const pollJobPlugin = { * @param {String} [name=''] * @param {String} [title=''] * @param {String} [description=''] + * @param {Boolean} [showSuccessMessage=true] * @param {String} [successMessage=Success] * @param {Function} [successMethod=() => {}] * @param {String} [errorMessage=Error] @@ -48,6 +50,7 @@ export const pollJobPlugin = { name = '', title = '', description = '', + showSuccessMessage = true, successMessage = i18n.global.t('label.success'), successMethod = () => {}, errorMessage = i18n.global.t('label.error'), @@ -87,22 +90,26 @@ export const pollJobPlugin = { }) options.originalPage = options.originalPage || this.$router.currentRoute.value.path - api('queryAsyncJobResult', { jobId }).then(json => { + getAPI('queryAsyncJobResult', { jobId }).then(json => { const result = json.queryasyncjobresultresponse eventBus.emit('update-job-details', { jobId, resourceId }) if (result.jobstatus === 1) { - var content = successMessage - if (successMessage === 'Success' && action && action.label) { - content = i18n.global.t(action.label) - } - if (name) { - content = content + ' - ' + name + if (showSuccessMessage) { + var content = successMessage + if (successMessage === 'Success' && action && action.label) { + content = i18n.global.t(action.label) + } + if (name) { + content = content + ' - ' + name + } + message.success({ + content, + key: jobId, + duration: 2 + }) + } else { + message.destroy(jobId) } - message.success({ - content, - key: jobId, - duration: 2 - }) store.dispatch('AddHeaderNotice', { key: jobId, title, @@ -217,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++ @@ -288,37 +296,26 @@ export const notifierPlugin = { close: (key) => notification.close(key), destroy: () => notification.destroy() } + + app.config.globalProperties.$messageConfigSuccess = function (msg, configrecord) { + if (configrecord.isdynamic) { + msg += `. ${this.$t('message.setting.update.delay')}` + } + message.success(msg) + } } } export const toLocaleDatePlugin = { install (app) { app.config.globalProperties.$toLocaleDate = function (date) { - var timezoneOffset = this.$store.getters.timezoneoffset - if (this.$store.getters.usebrowsertimezone) { - // Since GMT+530 is returned as -330 (mins to GMT) - timezoneOffset = new Date().getTimezoneOffset() / -60 - } - var milliseconds = Date.parse(date) - // e.g. "Tue, 08 Jun 2010 19:13:49 GMT", "Tue, 25 May 2010 12:07:01 UTC" - var dateWithOffset = new Date(milliseconds + (timezoneOffset * 60 * 60 * 1000)).toUTCString() - // e.g. "08 Jun 2010 19:13:49 GMT", "25 May 2010 12:07:01 UTC" - dateWithOffset = dateWithOffset.substring(dateWithOffset.indexOf(', ') + 2) - // e.g. "08 Jun 2010 19:13:49", "25 May 2010 12:10:16" - dateWithOffset = dateWithOffset.substring(0, dateWithOffset.length - 4) - return dateWithOffset + const { timezoneoffset, usebrowsertimezone } = this.$store.getters + return toLocaleDate({ date, timezoneoffset, usebrowsertimezone }) } app.config.globalProperties.$toLocalDate = function (date) { - var timezoneOffset = this.$store.getters.timezoneoffset - if (this.$store.getters.usebrowsertimezone) { - // Since GMT+530 is returned as -330 (mins to GMT) - timezoneOffset = new Date().getTimezoneOffset() / -60 - } - var milliseconds = Date.parse(date) - // e.g. "Tue, 08 Jun 2010 19:13:49 GMT", "Tue, 25 May 2010 12:07:01 UTC" - var dateWithOffset = new Date(milliseconds + (timezoneOffset * 60 * 60 * 1000)) - return dateWithOffset.toISOString() + const { timezoneoffset, usebrowsertimezone } = this.$store.getters + return toLocalDate({ date, timezoneoffset, usebrowsertimezone }).toISOString() } } } @@ -348,7 +345,7 @@ export const showIconPlugin = { if (resource) { resourceType = resource } - if (['zone', 'zones', 'template', 'iso', 'account', 'accountuser', 'vm', 'domain', 'project', 'vpc', 'guestnetwork'].includes(resourceType)) { + if (['zone', 'zones', 'template', 'iso', 'account', 'accountuser', 'vm', 'domain', 'project', 'vpc', 'guestnetwork', 'guestoscategory'].includes(resourceType)) { return true } else { return false @@ -390,6 +387,12 @@ export const resourceTypePlugin = { return 'publicip' case 'NetworkAcl': return 'acllist' + case 'KubernetesCluster': + return 'kubernetes' + case 'KubernetesSupportedVersion': + return 'kubernetesiso' + case 'ExtensionCustomAction': + return 'customaction' case 'SystemVm': case 'PhysicalNetwork': case 'Backup': @@ -417,6 +420,9 @@ export const resourceTypePlugin = { case 'AffinityGroup': case 'VpnCustomerGateway': case 'AutoScaleVmGroup': + case 'QuotaTariff': + case 'GuestOsCategory': + case 'Extension': return resourceType.toLowerCase() } return '' @@ -426,6 +432,9 @@ export const resourceTypePlugin = { var routePath = this.$getRouteFromResourceType(resourceType) if (!routePath) return '' var route = this.$router.resolve('/' + routePath) + if (routePath === 'kubernetes') { + return route?.meta?.icon[0] + } return route?.meta?.icon || '' } } @@ -454,33 +463,48 @@ export const localesPlugin = { } } -const KB = 1024 -const MB = 1024 * KB -const GB = 1024 * MB -const TB = 1024 * GB +const KiB = 1024 +const MiB = 1024 * KiB +const GiB = 1024 * MiB +const TiB = 1024 * GiB export const fileSizeUtilPlugin = { install (app) { + app.config.globalProperties.$bytesToGiB = function (bytes) { + if (bytes == null || bytes === 0) { + return 0 + } + return (bytes / GiB).toFixed(2) + } app.config.globalProperties.$bytesToHumanReadableSize = function (bytes) { if (bytes == null) { return '' } - if (bytes < KB && bytes >= 0) { + if (bytes < KiB && bytes >= 0) { return bytes + ' bytes' } - if (bytes < MB) { - return (bytes / KB).toFixed(2) + ' KB' - } else if (bytes < GB) { - return (bytes / MB).toFixed(2) + ' MB' - } else if (bytes < TB) { - return (bytes / GB).toFixed(2) + ' GB' + if (bytes < MiB) { + return (bytes / KiB).toFixed(2) + ' KiB' + } else if (bytes < GiB) { + return (bytes / MiB).toFixed(2) + ' MiB' + } else if (bytes < TiB) { + return (bytes / GiB).toFixed(2) + ' GiB' } else { - return (bytes / TB).toFixed(2) + ' TB' + return (bytes / TiB).toFixed(2) + ' TiB' } } } } +function isBase64 (str) { + try { + const decoded = new TextDecoder().decode(Uint8Array.from(atob(str), c => c.charCodeAt(0))) + return btoa(decoded) === str + } catch (err) { + return false + } +} + export const genericUtilPlugin = { install (app) { app.config.globalProperties.$isValidUuid = function (uuid) { @@ -489,11 +513,10 @@ export const genericUtilPlugin = { } app.config.globalProperties.$toBase64AndURIEncoded = function (text) { - const base64regex = /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/ - if (base64regex.test(text)) { + if (isBase64(text)) { return text } - return encodeURIComponent(btoa(unescape(encodeURIComponent(text)))) + return encodeURI(btoa(unescape(encodeURIComponent(text)))) } } } @@ -514,3 +537,87 @@ export function createPathBasedOnVmType (vmtype, virtualmachineid) { return path + virtualmachineid } + +export const dialogUtilPlugin = { + install (app) { + app.config.globalProperties.$resetConfigurationValueConfirm = function (configRecord, callback) { + Modal.confirm({ + title: i18n.global.t('label.reset.config.value'), + content: `${i18n.global.t('message.confirm.reset.configuration.value').replace('%x', configRecord.name)}`, + okText: i18n.global.t('label.yes'), + cancelText: i18n.global.t('label.no'), + okType: 'primary', + 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') + }) + } + } +} + +export const cpuArchitectureUtilPlugin = { + install (app) { + 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: 's390x', name: 'IBM Z 64 bits (s390x)' } + ] + return architectures.map(item => ({ ...item, description: item.name })) + } + } +} + +export const imagesUtilPlugin = { + install (app) { + app.config.globalProperties.$fetchTemplateTypes = function (hypervisor) { + const baseTypes = ['USER'] + if (hypervisor === 'External') { + return baseTypes.map(type => ({ id: type, name: type, description: type })) + } + baseTypes.push('VNF') + const adminTypes = ['SYSTEM', 'BUILTIN', 'ROUTING'] + const types = [...baseTypes] + if (store.getters.userInfo?.roletype === 'Admin') { + types.push(...adminTypes) + } + return types.map(type => ({ id: type, name: type, description: type })) + } + } +} + +export const extensionsUtilPlugin = { + install (app) { + app.config.globalProperties.$fetchCustomActionRoleTypes = function () { + const roleTypes = [] + const roleTypesList = ['Admin', 'Resource Admin', 'Domain Admin', 'User'] + roleTypesList.forEach((item) => { + roleTypes.push({ + id: item.replace(' ', ''), + description: item + }) + }) + return roleTypes + } + } +} + +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 new file mode 100644 index 000000000000..b8adbb93518a --- /dev/null +++ b/ui/src/utils/quota.js @@ -0,0 +1,124 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// Note: it could be retrieved from an API +export const QUOTA_TYPES = [ + { + id: 1, + type: 'RUNNING_VM' + }, + { + id: 2, + type: 'ALLOCATED_VM' + }, + { + id: 3, + type: 'IP_ADDRESS' + }, + { + id: 4, + type: 'NETWORK_BYTES_SENT' + }, + { + id: 5, + type: 'NETWORK_BYTES_RECEIVED' + }, + { + id: 6, + type: 'VOLUME' + }, + { + id: 7, + type: 'TEMPLATE' + }, + { + id: 8, + type: 'ISO' + }, + { + id: 9, + type: 'SNAPSHOT' + }, + { + id: 10, + type: 'SECURITY_GROUP' + }, + { + id: 11, + type: 'LOAD_BALANCER_POLICY' + }, + { + id: 12, + type: 'PORT_FORWARDING_RULE' + }, + { + id: 13, + type: 'NETWORK_OFFERING' + }, + { + id: 14, + type: 'VPN_USERS' + }, + { + id: 21, + type: 'VM_DISK_IO_READ' + }, + { + id: 22, + type: 'VM_DISK_IO_WRITE' + }, + { + id: 23, + type: 'VM_DISK_BYTES_READ' + }, + { + id: 24, + type: 'VM_DISK_BYTES_WRITE' + }, + { + id: 25, + type: 'VM_SNAPSHOT' + }, + { + id: 26, + type: 'VOLUME_SECONDARY' + }, + { + id: 27, + type: 'VM_SNAPSHOT_ON_PRIMARY' + }, + { + id: 28, + type: 'BACKUP' + }, + { + id: 29, + type: 'BUCKET' + }, + { + id: 30, + type: 'NETWORK' + }, + { + id: 31, + type: 'VPC' + } +] + +export const getQuotaTypes = () => { + return QUOTA_TYPES.sort((a, b) => a.type.localeCompare(b.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 c2fe04ab9d1a..2317aac04465 100644 --- a/ui/src/utils/request.js +++ b/ui/src/utils/request.js @@ -51,10 +51,10 @@ const err = (error) => { }) } if (response.status === 401) { - if (response.config && response.config.params && ['listIdps', 'cloudianIsEnabled'].includes(response.config.params.command)) { + 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')) { @@ -149,6 +149,15 @@ const err = (error) => { service.interceptors.request.use(config => { source = sourceToken.getSource() config.cancelToken = source.token + + handleGetRequestParams(config) + + handlePostRequestParams(config) + + return config +}, err) + +function handleGetRequestParams (config) { if (config && config.params) { config.params.response = 'json' const project = vueProps.$localStorage.get(CURRENT_PROJECT) @@ -160,11 +169,30 @@ service.interceptors.request.use(config => { } } if (config.params.ignoreproject !== undefined) { - config.params.ignoreproject = null + delete config.params.ignoreproject } } - return config -}, err) +} + +function handlePostRequestParams (config) { + if (config && config.data && config.data instanceof URLSearchParams) { + const project = vueProps.$localStorage.get(CURRENT_PROJECT) + const command = config.data.get('command') + const hasProjectId = config.data.has('projectid') + const ignoreProject = config.data.has('ignoreproject') + + if (!hasProjectId && !ignoreProject && project && project.id) { + if (command === 'listTags') { + config.data.append('projectid', '-1') + } else if (command !== 'assignVirtualMachine') { + config.data.append('projectid', project.id) + } + } + if (config.data.has('ignoreproject')) { + config.data.delete('ignoreproject') + } + } +} // response interceptor service.interceptors.response.use((response) => { diff --git a/ui/src/utils/sort.js b/ui/src/utils/sort.js index b2f3d5451cae..5e893fb58ec2 100644 --- a/ui/src/utils/sort.js +++ b/ui/src/utils/sort.js @@ -23,13 +23,19 @@ function filterNumber (value) { } function stringComparator (a, b) { - return a.toString().localeCompare(b.toString()) + return a.toString().localeCompare(b.toString(), undefined, { numeric: true }) } function numericComparator (a, b) { return filterNumber(a) < filterNumber(b) ? 1 : -1 } +function metricComparator (ma, mb) { + var a = ('' + ma).replace(/((%)|(Ghz)|(Mhz)|(MiB)|(GiB)|(GB)).*$/g, '') + var b = ('' + mb).replace(/((%)|(Ghz)|(Mhz)|(MiB)|(GiB)|(GB)).*$/g, '') + return parseFloat(a) < parseFloat(b) ? 1 : -1 +} + function ipV4AddressCIDRComparator (a, b) { a = a.split(/[./]/gm) b = b.split(/[./]/gm) @@ -76,6 +82,10 @@ function isNumeric (obj) { return !Array.isArray(obj) && !isNaN(filterNumber(obj)) } +function isMetric (value) { + return /^[0-9\\. ]*((%)|(Ghz)|(Mhz)|(MiB)|(GiB)|(GB))/.test(value) +} + /** * Compare elements, attempting to determine type of element to get the best comparison * @@ -97,6 +107,9 @@ export function genericCompare (a, b) { if (isNumeric(a) && isNumeric(b)) { comparator = numericComparator } + if (isMetric(a) && isMetric(b)) { + comparator = metricComparator + } return comparator(a, b) } diff --git a/ui/src/utils/util.js b/ui/src/utils/util.js index a55d05dbd8fe..3c51096ac53e 100644 --- a/ui/src/utils/util.js +++ b/ui/src/utils/util.js @@ -15,6 +15,8 @@ // specific language governing permissions and limitations // under the License. +import semver from 'semver' + export function timeFix () { const time = new Date() const hour = time.getHours() @@ -68,3 +70,56 @@ export function sanitizeReverse (value) { .replace(/</g, '<') .replace(/>/g, '>') } + +export function getParsedVersion (version) { + version = version.split('-')[0] + if (semver.valid(version) === null) { + version = version.split('.').slice(1).join('.') + } + return version +} + +export function toCsv ({ keys = null, data = null, columnDelimiter = ',', lineDelimiter = '\n' }) { + if (data === null || !data.length) { + return null + } + + let result = '' + result += keys.join(columnDelimiter) + result += lineDelimiter + + data.forEach(item => { + keys.forEach(key => { + if (item[key] === undefined) { + item[key] = '' + } + result += typeof item[key] === 'string' && item[key].includes(columnDelimiter) ? `"${item[key]}"` : item[key] + result += columnDelimiter + }) + result = result.slice(0, -1) + result += lineDelimiter + }) + + return result +} + +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/utils/zone.js b/ui/src/utils/zone.js new file mode 100644 index 000000000000..266bf9df4451 --- /dev/null +++ b/ui/src/utils/zone.js @@ -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. + +import store from '@/store' + +export function isZoneCreated () { + if (!store.getters.zones || store.getters.zones.length === 0) { + return false + } + return true +} diff --git a/ui/src/views/AutogenView.vue b/ui/src/views/AutogenView.vue index 2404bc0b6d40..f4aa842d8f2b 100644 --- a/ui/src/views/AutogenView.vue +++ b/ui/src/views/AutogenView.vue @@ -17,10 +17,19 @@ @@ -146,7 +182,8 @@ @refresh-data="fetchData" @poll-action="pollActionCompletion" @close-action="closeAction" - @cancel-bulk-action="handleCancel"/> + @cancel-bulk-action="handleCancel" + /> @@ -170,25 +207,39 @@ v-if="currentAction.docHelp || $route.meta.docHelp" style="margin-left: 5px" :href="$config.docBase + '/' + (currentAction.docHelp || $route.meta.docHelp)" - target="_blank"> + target="_blank" + > - +
+ type="error" + > - + @@ -211,17 +262,26 @@ :pagination="true" style="overflow-y: auto" > +
-
+
-
+ layout="vertical" + > +
= 0 }" > - {{ }} + {{ }} + :label="opt" + > {{ opt }} @@ -271,11 +344,15 @@ }" v-focus="fieldIndex === firstIndex" > - {{ }} + {{ }} + :label="opt.name || opt.description || opt.traffictype || opt.publicip" + > {{ opt.name || opt.description || opt.traffictype || opt.publicip }} @@ -291,44 +368,96 @@ }" v-focus="fieldIndex === firstIndex" > - {{ }} - + {{ }} +
- + - + - + - + - + - + - + - + - + - + - + - + {{ opt.name || opt.description || opt.traffictype || opt.publicip }}
@@ -350,10 +479,14 @@ - {{ opt.name && opt.type ? opt.name + ' (' + opt.type + ')' : opt.name || opt.description }} + :label="(opt.name && opt.type ? opt.name + ' (' + opt.type + ')' : opt.name || opt.description)" + > + {{ (opt.name && opt.type ? opt.name + ' (' + opt.type + ')' : opt.name || opt.description) }} + + :placeholder="field.description" + />
-
+
{{ $t('label.cancel') }} - {{ $t('label.ok') }} + {{ $t('label.ok') }}
@@ -393,28 +534,42 @@
-
+
- + + :tabs="$route.meta.tabs" + />
-
+
+ + /> + showQuickJumper + > @@ -439,14 +595,15 @@ :selectedItems="selectedItems" :selectedColumns="bulkColumns" :message="modalInfo" - @handle-cancel="handleCancel" /> + @handle-cancel="handleCancel" + />
+ + diff --git a/ui/src/views/auth/Login.vue b/ui/src/views/auth/Login.vue index 8503f71082b5..24065f47b1aa 100644 --- a/ui/src/views/auth/Login.vue +++ b/ui/src/views/auth/Login.vue @@ -91,6 +91,18 @@ type="text" :placeholder="$t('label.domain')" v-model:value="form.domain" + > + + + + + -
-

{{ $t('label.accounttype') }}

- - {{ $t('label.account') }} - {{ $t('label.project') }} - -
- -
-

*{{ $t('label.domain') }}

- - - - - - {{ domain.path || domain.name || domain.description }} - - - -
- - - - +

{{ $t('label.network') }}

@@ -144,8 +65,9 @@ + + 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 981c2b0ab183..9e8ebb8a4d59 100644 --- a/ui/src/views/compute/CreateAutoScaleVmGroup.vue +++ b/ui/src/views/compute/CreateAutoScaleVmGroup.vue @@ -34,72 +34,70 @@
{{ $t('message.select.a.zone') }}
-
- -
- - - - - - - {{ zoneItem.name }} - - - - -
-
-
- - - - - - {{ zone1.name }} - - - +
+ + + + + diff --git a/ui/src/views/infra/Resources.vue b/ui/src/views/infra/Resources.vue index 36416b02603a..0055343b30df 100644 --- a/ui/src/views/infra/Resources.vue +++ b/ui/src/views/infra/Resources.vue @@ -31,6 +31,34 @@ :percent="parseFloat(item.percentused)" :format="p => parseFloat(item.percentused).toFixed(2) + '%'" />
+ + + + + + +
@@ -38,7 +66,7 @@ + + diff --git a/ui/src/views/infra/UsageRecords.vue b/ui/src/views/infra/UsageRecords.vue new file mode 100644 index 000000000000..f206c11b7134 --- /dev/null +++ b/ui/src/views/infra/UsageRecords.vue @@ -0,0 +1,850 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + + + + + + diff --git a/ui/src/views/infra/ZoneUpdate.vue b/ui/src/views/infra/ZoneUpdate.vue new file mode 100644 index 000000000000..26968f251270 --- /dev/null +++ b/ui/src/views/infra/ZoneUpdate.vue @@ -0,0 +1,268 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + + + + + + diff --git a/ui/src/views/infra/network/DedicatedVLANTab.vue b/ui/src/views/infra/network/DedicatedVLANTab.vue index 7343b30b692e..9d47138f0ae7 100644 --- a/ui/src/views/infra/network/DedicatedVLANTab.vue +++ b/ui/src/views/infra/network/DedicatedVLANTab.vue @@ -174,7 +174,7 @@ + + diff --git a/ui/src/views/infra/zone/BgpPeersTab.vue b/ui/src/views/infra/zone/BgpPeersTab.vue new file mode 100644 index 000000000000..e55daf02a413 --- /dev/null +++ b/ui/src/views/infra/zone/BgpPeersTab.vue @@ -0,0 +1,762 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + + + + + + diff --git a/ui/src/views/infra/zone/IpAddressRangeForm.vue b/ui/src/views/infra/zone/IpAddressRangeForm.vue index 22332952622f..4c9af84a8f8d 100644 --- a/ui/src/views/infra/zone/IpAddressRangeForm.vue +++ b/ui/src/views/infra/zone/IpAddressRangeForm.vue @@ -30,10 +30,17 @@ :columns="columns" :pagination="false" style="margin-bottom: 24px; width: 100%" > -